diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index 334812e8341..e94fdc9fbd4 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@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: jtreg/installed key: jtreg-${{ steps.version.outputs.value }} - name: 'Checkout the JTReg source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 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@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 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 6f6bbdabb68..5d015079f54 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@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 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@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 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 d531358b7dd..eca7e1b29d8 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@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 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 55fa0e842d2..3884d169d9c 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@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles continue-on-error: true - name: 'Download bundles artifact (retry)' - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 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@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 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 bc53fa2a3b1..da10e7660a1 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@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 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 8c75ae10c7f..35d1f93bd3c 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@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 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 4082aad0c1a..79103b2fe46 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -35,7 +35,7 @@ runs: steps: - name: 'Install MSYS2 on x86.x64' id: msys2-x64 - uses: msys2/setup-msys2@v2.31.0 + uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: install: 'autoconf tar unzip zip make' path-type: minimal @@ -44,7 +44,7 @@ runs: - name: 'Install MSYS2 on ARM64' id: msys2-arm64 - uses: msys2/setup-msys2@v2.31.0 + uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.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 94308002ea7..dc6acdbec3e 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@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }}${{ inputs.bundle-suffix }} path: bundles diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index 6863da9016e..545993b5387 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@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Install toolchain and dependencies' run: | diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index c80f676864e..84025981f3a 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -87,7 +87,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get the BootJDK' id: bootjdk @@ -115,7 +115,7 @@ jobs: - name: 'Check cache for sysroot' id: get-cached-sysroot - uses: actions/cache@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 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 a77ebece7e2..9c65492e838 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -92,7 +92,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 435576f4afd..6c13f32d10e 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@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 002cbe7cd56..e95f36a388a 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -86,7 +86,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcb9ea6e0b8..57f81f7fd51 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,7 +76,7 @@ jobs: steps: - name: 'Checkout the scripts' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: sparse-checkout: | .github diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6270e44d746..054bd00beb9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -131,7 +131,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 @@ -244,7 +244,7 @@ jobs: if: always() - name: 'Upload test results' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: path: results name: ${{ steps.package.outputs.artifact-name }} @@ -252,7 +252,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@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: core.setFailed('${{ steps.run-tests.outputs.error-message }}') if: steps.run-tests.outputs.failure == 'true' diff --git a/make/Images.gmk b/make/Images.gmk index 8008cfa6779..a09ac7e3bc6 100644 --- a/make/Images.gmk +++ b/make/Images.gmk @@ -142,8 +142,7 @@ define CreateCDSArchive $1_$2_COOPS_OPTION := $(if $(findstring _nocoops, $2),-XX:-UseCompressedOops) # enable and also explicitly disable coh as needed. ifeq ($(call isTargetCpuBits, 64), true) - $1_$2_NOCOH_OPTION := -XX:+UnlockExperimentalVMOptions \ - $(if $(findstring _nocoh, $2),-XX:-UseCompactObjectHeaders,-XX:+UseCompactObjectHeaders) + $1_$2_NOCOH_OPTION := $(if $(findstring _nocoh, $2),-XX:-UseCompactObjectHeaders,-XX:+UseCompactObjectHeaders) endif $1_$2_DUMP_EXTRA_ARG := $$($1_$2_COOPS_OPTION) $$($1_$2_NOCOH_OPTION) $1_$2_DUMP_TYPE := $(if $(findstring _nocoops, $2),-NOCOOPS,)$(if $(findstring _nocoh, $2),-NOCOH,) diff --git a/make/autoconf/basic.m4 b/make/autoconf/basic.m4 index bb6908d9194..1591df46a91 100644 --- a/make/autoconf/basic.m4 +++ b/make/autoconf/basic.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 @@ -327,14 +327,6 @@ AC_DEFUN_ONCE([BASIC_SETUP_DEVKIT], elif test -d "$DEVKIT_ROOT/$host/sys-root"; then SYSROOT="$DEVKIT_ROOT/$host/sys-root" fi - - if test "x$DEVKIT_ROOT" != x; then - DEVKIT_LIB_DIR="$DEVKIT_ROOT/lib" - if test "x$OPENJDK_TARGET_CPU_BITS" = x64; then - DEVKIT_LIB_DIR="$DEVKIT_ROOT/lib64" - fi - AC_SUBST(DEVKIT_LIB_DIR) - fi fi # You can force the sysroot if the sysroot encoded into the compiler tools diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index 66ef94d48a8..db6c893c8b3 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -148,7 +148,7 @@ AC_DEFUN([BASIC_CHECK_MAKE_VERSION], if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then MAKE_EXPECTED_ENV='cygwin' elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys2"; then - MAKE_EXPECTED_ENV='msys' + MAKE_EXPECTED_ENV='cygwin|msys' elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl1" || test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl2"; then if test "x$OPENJDK_BUILD_CPU" = "xaarch64"; then MAKE_EXPECTED_ENV='aarch64-.*-linux-gnu' @@ -159,7 +159,7 @@ AC_DEFUN([BASIC_CHECK_MAKE_VERSION], AC_MSG_ERROR([Unknown Windows environment]) fi MAKE_BUILT_FOR=`$MAKE_CANDIDATE --version | $GREP -i 'built for'` - IS_MAKE_CORRECT_ENV=`$ECHO $MAKE_BUILT_FOR | $GREP $MAKE_EXPECTED_ENV` + IS_MAKE_CORRECT_ENV=`$ECHO $MAKE_BUILT_FOR | $GREP -E $MAKE_EXPECTED_ENV` else # Not relevant for non-Windows IS_MAKE_CORRECT_ENV=true diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index ff10828731e..1da98f5cdeb 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -81,6 +81,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], fi if test "x$ENABLE_LINKTIME_GC" = xtrue; then + 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 fi @@ -93,7 +94,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then BASIC_LDFLAGS="-opt:ref" BASIC_LDFLAGS_JDK_ONLY="-incremental:no" - BASIC_LDFLAGS_JVM_ONLY="-opt:icf,8 -subsystem:windows" + BASIC_LDFLAGS_JVM_ONLY="-opt:noicf -subsystem:windows" LDFLAGS_LTO="-LTCG:INCREMENTAL" fi diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4 index faaf229eacd..89f9bf425e1 100644 --- a/make/autoconf/lib-tests.m4 +++ b/make/autoconf/lib-tests.m4 @@ -28,7 +28,7 @@ ################################################################################ # Minimum supported versions -JTREG_MINIMUM_VERSION=8.2.1 +JTREG_MINIMUM_VERSION=8.3 GTEST_MINIMUM_VERSION=1.14.0 ################################################################################ diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 90d5d795626..28aea489f7e 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -174,18 +174,6 @@ AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_CPU], VAR_CPU_BITS=64 VAR_CPU_ENDIAN=big ;; - sparc) - VAR_CPU=sparc - VAR_CPU_ARCH=sparc - VAR_CPU_BITS=32 - VAR_CPU_ENDIAN=big - ;; - sparcv9|sparc64) - VAR_CPU=sparcv9 - VAR_CPU_ARCH=sparc - VAR_CPU_BITS=64 - VAR_CPU_ENDIAN=big - ;; *) AC_MSG_ERROR([unsupported cpu $1]) ;; diff --git a/make/common/modules/GensrcStreamPreProcessing.gmk b/make/common/modules/GensrcStreamPreProcessing.gmk index a48e3c98d4b..eb92ed99ed4 100644 --- a/make/common/modules/GensrcStreamPreProcessing.gmk +++ b/make/common/modules/GensrcStreamPreProcessing.gmk @@ -116,7 +116,7 @@ Conv_A = \ # Return integer type with same size as the type Conv_memtype = \ - $(if $(filter float, $1), int, $(if $(filter double, $1), long, $1)) + $(if $(filter float, $1), int, $(if $(filter double, $1), long, $(if $(filter boolean, $1), byte, $1))) # Return capitalized integer type with same size as the type Conv_Memtype = \ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 9aee8e87e3c..6c5805f0764 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -26,7 +26,7 @@ # Versions and download locations for dependencies used by GitHub Actions (GHA) GTEST_VERSION=1.14.0 -JTREG_VERSION=8.2.1+1 +JTREG_VERSION=8.3+1 LINUX_X64_BOOT_JDK_EXT=tar.gz LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_linux-x64_bin.tar.gz diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 20315cda97d..32f07325c05 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -644,7 +644,7 @@ var getJibProfilesProfiles = function (input, common, data) { // Bootcycle profiles runs the build with itself as the boot jdk. This can // be done in two ways. Either using the builtin bootcycle target in the // build system. Or by supplying the main jdk build as bootjdk to configure. - [ "linux-x64", "macosx-x64", "windows-x64", "linux-aarch64" ] + [ "linux-x64", "macosx-aarch64", "macosx-x64", "windows-x64", "linux-aarch64" ] .forEach(function (name) { var bootcycleName = name + "-bootcycle"; var bootcyclePrebuiltName = name + "-bootcycle-prebuilt"; @@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) { jtreg: { server: "jpg", product: "jtreg", - version: "8.2.1", + version: "8.3", build_number: "1", - file: "bundles/jtreg-8.2.1+1.zip", + file: "bundles/jtreg-8.3+1.zip", environment_name: "JT_HOME", environment_path: input.get("jtreg", "home_path") + "/bin", configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"), diff --git a/make/conf/module-loader-map.conf b/make/conf/module-loader-map.conf index 35b9345ed8f..2d528c4b8ed 100644 --- a/make/conf/module-loader-map.conf +++ b/make/conf/module-loader-map.conf @@ -28,7 +28,7 @@ # PLATFORM_MODULES are modules defined by the platform loader # # All other modules not declared below are defined by the application loader -# and are not included in JRE. +# and are not included in the JDK runtime image. BOOT_MODULES= \ java.base \ @@ -52,7 +52,6 @@ BOOT_MODULES= \ jdk.nio.mapmode \ jdk.sctp \ jdk.unsupported \ - jdk.naming.rmi \ # # Modules that directly or indirectly requiring upgradeable modules @@ -78,6 +77,7 @@ PLATFORM_MODULES= \ jdk.httpserver \ jdk.localedata \ jdk.naming.dns \ + jdk.naming.rmi \ jdk.security.auth \ jdk.security.jgss \ jdk.xml.dom \ diff --git a/make/devkit/Sysroot.gmk b/make/devkit/Sysroot.gmk index 13395172074..a0576ed571b 100644 --- a/make/devkit/Sysroot.gmk +++ b/make/devkit/Sysroot.gmk @@ -88,8 +88,7 @@ SRCDIR := $(OUTPUT_ROOT)/src DOWNLOAD_RPMS_MARKER := $(BUILDDIR)/download-rpms.marker RPMS_UNPACKED_MARKER := $(BUILDDIR)/rpms_unpacked.marker -UNPATCHED_SYSROOT_MARKER := $(BUILDDIR)/sysroot_unpatched.marker -PATCHED_SYSROOT_MARKER := $(BUILDDIR)/sysroot_patched.marker +SYSROOT_MARKER := $(BUILDDIR)/sysroot.marker ################################################################################ # Download RPMs @@ -101,10 +100,6 @@ else endif RPM_ARCHS := $(RPM_ARCH) noarch -ifeq ($(ARCH), x86_64) - # Enable mixed mode. - RPM_ARCHS += i386 i686 -endif EMPTY := SPACE := $(EMPTY) $(EMPTY) @@ -166,43 +161,12 @@ $(RPMS_UNPACKED_MARKER): $(DOWNLOAD_RPMS_MARKER) ################################################################################ -$(UNPATCHED_SYSROOT_MARKER): $(RPMS_UNPACKED_MARKER) +$(SYSROOT_MARKER): $(RPMS_UNPACKED_MARKER) touch $@ -################################################################################ -# Patch sysroot - -# Note: MUST create a /usr/lib even if not really needed. -# gcc will use a path relative to it to resolve lib64. (x86_64). -# we're creating multi-lib compiler with 32bit libc as well, so we should -# have it anyway, but just to make sure... -# Patch GNU ld scripts to force linking against libraries in the sysroot -# and not the ones installed on the build machine. - -LD_SCRIPT_PATCHES := \ - -e 's|/usr/lib64/||g' \ - -e 's|/usr/lib/||g' \ - -e 's|/lib64/||g' \ - -e 's|/lib/||g' \ - # - -$(PATCHED_SYSROOT_MARKER): $(UNPATCHED_SYSROOT_MARKER) - @echo Patching GNU ld scripts - @( \ - for f in $$(find $(SYSROOT) -name "*.so" -type f 2>/dev/null); do \ - if grep -Iq 'GNU ld script' "$$f"; then \ - sed $(LD_SCRIPT_PATCHES) "$$f" > "$$f.tmp" && \ - mv "$$f.tmp" "$$f"; \ - fi; \ - done \ - ) - @mkdir -p $(SYSROOT)/usr/lib - @touch $@ - ################################################################################ download-rpms: $(DOWNLOAD_RPMS_MARKER) -unpatched-sysroot: $(UNPATCHED_SYSROOT_MARKER) -sysroot: $(PATCHED_SYSROOT_MARKER) +sysroot: $(SYSROOT_MARKER) -.PHONY: download-rpms unpatched-sysroot sysroot +.PHONY: download-rpms sysroot diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index 835ada3a082..aac7489020e 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -103,16 +103,6 @@ ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),) endif ################################################################################ -# Define common directories and files - -# Ensure we have 32-bit libs also for x64. We enable mixed-mode. -ifeq (x86_64,$(ARCH)) - LIBDIRS := lib64 lib - CFLAGS_lib := -m32 -else - LIBDIRS := lib -endif - # Define directories DOWNLOAD := $(OUTPUT_ROOT)/download SRCDIR := $(OUTPUT_ROOT)/src @@ -200,23 +190,19 @@ TOOLS ?= $(call declare_tools,_FOR_TARGET,$(TARGET)-) ################################################################################ -# Create a TARGET bfd + libiberty only. -# Configure one or two times depending on mulitlib arch. -# If multilib, the second should be 32-bit, and we resolve -# CFLAG_ to most likely -m32. -define mk_bfd - $$(info Libs for $(1)) - $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile: \ - CFLAGS += $$(CFLAGS_$(1)) - $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile: \ - LIBDIRS = --libdir=$(TARGETDIR)/$(1) +ifeq (x86_64,$(ARCH)) + LIBDIR := lib64 +else + LIBDIR := lib +endif - BFDLIB += $$(TARGETDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1)).done - BFDMAKES += $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile -endef +$(BUILDDIR)/$(BINUTILS_VER)-$(LIBDIR)/Makefile: + CFLAGS += CFLAGS_$(LIBDIR) +$(BUILDDIR)/$(BINUTILS_VER)-$(LIBDIR)/Makefile: + LIBDIR = --libdir=$(TARGETDIR)/$(LIBDIR) -# Create one set of bfds etc for each multilib arch -$(foreach l,$(LIBDIRS),$(eval $(call mk_bfd,$(l)))) +BFDLIB += $(TARGETDIR)/$(BINUTILS_VER)-$(LIBDIR).done +BFDMAKES += $(BUILDDIR)/$(BINUTILS_VER)-$(LIBDIR)/Makefile # Only build these two libs. $(BFDLIB): MAKECMD = all-libiberty all-bfd @@ -228,7 +214,7 @@ $(BFDMAKES): CONFIG = --target=$(TARGET) \ --host=$(TARGET) --build=$(BUILD) \ --prefix=$(TARGETDIR) \ --with-sysroot=$(SYSROOT) \ - $(LIBDIRS) + $(LIBDIR) $(BFDMAKES): TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-) @@ -243,11 +229,8 @@ $(GCC) \ $(CCACHE): ENVS += $(TOOLS) # libdir to work around hateful bfd stuff installing into wrong dirs... -# ensure we have 64 bit bfd support in the HOST library. I.e our -# compiler on i686 will know 64 bit symbols, BUT later -# we build just the libs again for TARGET, then with whatever the arch -# wants. -$(BUILDDIR)/$(BINUTILS_VER)/Makefile: CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS)) +# ensure we have 64 bit bfd support in the HOST library. +$(BUILDDIR)/$(BINUTILS_VER)/Makefile: CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(LIBDIR) ifeq ($(filter $(ARCH), s390x riscv64 ppc64le), ) # gold compiles but cannot link properly on s390x @ gcc 13.2 and Fedore 41 @@ -256,10 +239,6 @@ ifeq ($(filter $(ARCH), s390x riscv64 ppc64le), ) LINKER_CONFIG_ENABLE_GOLD := --enable-gold=default endif -ifeq ($(filter riscv64 ppc64le s390x armhfp, $(ARCH)), ) - ENABLE_MULTILIB := --enable-multilib -endif - # Makefile creation. Simply run configure in build dir. # Setting CFLAGS to -O2 generates a much faster ld. $(BFDMAKES) \ @@ -274,8 +253,8 @@ $(BUILDDIR)/$(BINUTILS_VER)/Makefile: $(BINUTILS_CFG) $(LINKER_CONFIG_ENABLE_GOLD) \ --with-sysroot=$(SYSROOT) \ --disable-nls \ + --disable-gprofng \ --program-prefix=$(TARGET)- \ - $(ENABLE_MULTILIB) \ --enable-threads \ --enable-plugins \ ) > $(@D)/log.config 2>&1 @@ -340,15 +319,12 @@ ifeq ($(ARCH), riscv64) $(BUILDDIR)/$(GCC_VER)/Makefile: CONFIG += --disable-libsanitizer endif -ifneq ($(filter riscv64 ppc64le s390x armhfp, $(ARCH)), ) - # We only support 64-bit on these platforms anyway - CONFIG += --disable-multilib -endif +# We don't need to support multilib +CONFIG += --disable-multilib # Want: # c,c++ # shared libs -# multilib (-m32/-m64 on x64) # skip native language. # and link and assemble with the binutils we created # earlier, so --with-gnu* @@ -438,14 +414,12 @@ ifeq ($(HOST),$(TARGET)) # "Solve" this by create links from the target libdirs to where they are. $(LINK_LIBS_MARKER): $(GCC) @echo -n 'Creating library symlinks...' - @for l in $(LIBDIRS); do \ - for f in `cd $(PREFIX)/$$l && ls`; do \ - if [ ! -e $(TARGETDIR)/$$l/$$f ]; then \ - mkdir -p $(TARGETDIR)/$$l && \ - cd $(TARGETDIR)/$$l/ && \ - ln -fs ../../$$l/$$f $$f; \ - fi \ - done \ + @for f in `cd $(PREFIX)/$(LIBDIR) && ls`; do \ + if [ ! -e $(TARGETDIR)/$(LIBDIR)/$$f ]; then \ + mkdir -p $(TARGETDIR)/$(LIBDIR) && \ + cd $(TARGETDIR)/$(LIBDIR)/ && \ + ln -fs ../../$(LIBDIR)/$$f $$f; \ + fi \ done @touch $@ @echo 'done' diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 9477b0925d2..7dc5fd676a1 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -173,6 +173,10 @@ ifeq ($(call check-jvm-feature, link-time-opt), true) ifneq ($(call isCompiler, microsoft), true) JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) endif + # avoid elimination of Metadata vtable when using LTO (important for serviceability agent) + ifeq ($(call isCompiler, gcc), true) + JVM_LDFLAGS_FEATURES += -Wl,--undefined=_ZTV8Metadata + endif else JVM_LTO := false ifeq ($(call isCompiler, gcc), true) diff --git a/make/hotspot/lib/JvmOverrideFiles.gmk b/make/hotspot/lib/JvmOverrideFiles.gmk index 80f3582043c..b417cc19b03 100644 --- a/make/hotspot/lib/JvmOverrideFiles.gmk +++ b/make/hotspot/lib/JvmOverrideFiles.gmk @@ -33,10 +33,6 @@ ifeq ($(INCLUDE), true) ifeq ($(TOOLCHAIN_TYPE), gcc) BUILD_LIBJVM_vmStructs.cpp_CXXFLAGS := -fno-var-tracking-assignments - ifeq ($(DEBUG_LEVEL), release) - # Need extra inlining to collapse shared marking code into the hot marking loop - BUILD_LIBJVM_shenandoahMark.cpp_CXXFLAGS := --param inline-unit-growth=1000 - endif # disable lto in g1ParScanThreadState because of special inlining/flattening used there ifeq ($(call check-jvm-feature, link-time-opt), true) BUILD_LIBJVM_g1ParScanThreadState.cpp_CXXFLAGS := -fno-lto diff --git a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java index 84657ae94f0..8e9635ab519 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java @@ -29,6 +29,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Comparator; import java.util.Formatter; import java.util.HashSet; import java.util.HashMap; @@ -39,6 +40,7 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.stream.Collectors; +import static java.util.ResourceBundle.Control; class ResourceBundleGenerator implements BundleGenerator { // preferred timezones - keeping compatibility with JDK1.1 3 letter abbreviations @@ -69,6 +71,9 @@ class ResourceBundleGenerator implements BundleGenerator { // For duplicated values private static final String META_VALUE_PREFIX = "metaValue_"; + // locales in the base module + private final Set baseModuleLocales = new HashSet<>(); + @Override public void generateBundle(String packageName, String baseName, String localeID, Map map, BundleType type) throws IOException { @@ -80,8 +85,15 @@ class ResourceBundleGenerator implements BundleGenerator { return; } - // Assume that non-base resources go into jdk.localedata - if (!CLDRConverter.isBaseModule) { + if (CLDRConverter.isBaseModule) { + if (!localeID.equals("root")) { + baseModuleLocales.addAll( + Control.getControl(Control.FORMAT_DEFAULT) + .getCandidateLocales("", + Locale.forLanguageTag(CLDRConverter.toLanguageTag(localeID)))); + } + } else { + // Assume that non-base resources go into jdk.localedata dirName = dirName + File.separator + "ext"; packageName = packageName + ".ext"; } @@ -284,6 +296,7 @@ class ResourceBundleGenerator implements BundleGenerator { import java.util.HashMap; import java.util.Locale; import java.util.Map; + import java.util.Set; import sun.util.locale.provider.LocaleDataMetaInfo; import sun.util.locale.provider.LocaleProviderAdapter; @@ -296,6 +309,7 @@ class ResourceBundleGenerator implements BundleGenerator { out.printf(""" private static final Map parentLocalesMap = HashMap.newHashMap(%d); private static final Map languageAliasMap = HashMap.newHashMap(%d); + private static final Set baseModuleLocales; static final boolean nonlikelyScript = %s; // package access from CLDRLocaleProviderAdapter static { @@ -322,7 +336,23 @@ class ResourceBundleGenerator implements BundleGenerator { CLDRConverter.handlerSupplMeta.getLanguageAliasData().forEach((key, value) -> { out.printf(" languageAliasMap.put(\"%s\", \"%s\");\n", CLDRConverter.escape(key), CLDRConverter.escape(value)); }); - out.printf(" }\n\n"); + out.println(); + + // for baseModuleLocales + out.printf(" baseModuleLocales = Set.of(\n"); + out.printf(" %s", + baseModuleLocales.stream() + .map(Locale::toLanguageTag) + .sorted(Comparator.comparing(l -> l.equals("und") ? "" : l)) + .map(l -> switch(l) { + case "und" -> "Locale.ROOT"; + case "en" -> "Locale.ENGLISH"; + case "en-US" -> "Locale.US"; + default -> "Locale.forLanguageTag(\"" + l + "\")"; + }) + .collect(Collectors.joining(",\n "))); + out.printf("\n );"); + out.println("\n }\n"); // end of static initializer block. @@ -391,6 +421,10 @@ class ResourceBundleGenerator implements BundleGenerator { return parentLocalesMap; } + public Set baseModuleLocales() { + return baseModuleLocales; + } + // package access from CLDRLocaleProviderAdapter Map likelyScriptMap() { return CLDRMapHolder.likelyScriptMap; diff --git a/make/modules/java.base/gensrc/GensrcVarHandles.gmk b/make/modules/java.base/gensrc/GensrcVarHandles.gmk index 341a8c9dc2c..4b1697fd354 100644 --- a/make/modules/java.base/gensrc/GensrcVarHandles.gmk +++ b/make/modules/java.base/gensrc/GensrcVarHandles.gmk @@ -111,7 +111,7 @@ define GenerateVarHandleMemorySegment $1_KEYS += CAS endif ifneq ($$(filter boolean byte, $1),) - $1_KEYS += byte + $1_KEYS += ByteOrBoolean endif ifneq ($$(filter float double, $1),) $1_KEYS += floatingPoint diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index 316103be4cd..87a4460a972 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.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 @@ -35,8 +35,6 @@ ifeq ($(INCLUDE), true) $(eval $(call SetupJdkLibrary, BUILD_LIBVERIFY, \ NAME := verify, \ OPTIMIZATION := HIGH, \ - DISABLED_WARNINGS_gcc_check_code.c := unused-variable, \ - DISABLED_WARNINGS_clang_check_code.c := unused-variable, \ EXTRA_HEADER_DIRS := libjava, \ JDK_LIBS := libjvm, \ )) @@ -59,8 +57,6 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \ ProcessImpl_md.c_CFLAGS := $(VERSION_CFLAGS), \ java_props_md.c_CFLAGS := \ -DARCHPROPNAME='"$(OPENJDK_TARGET_CPU_OSARCH)"', \ - DISABLED_WARNINGS_gcc_ProcessImpl_md.c := unused-result, \ - DISABLED_WARNINGS_clang_TimeZone_md.c := unused-variable, \ JDK_LIBS := libjvm, \ LIBS_linux := $(LIBDL), \ LIBS_aix := $(LIBDL) $(LIBM), \ @@ -77,7 +73,7 @@ TARGETS += $(BUILD_LIBJAVA) ## Build libzip ################################################################################ -BUILD_LIBZIP_EXCLUDES := +LIBZIP_EXCLUDES := ifeq ($(USE_EXTERNAL_LIBZ), true) LIBZIP_EXCLUDES += zlib endif diff --git a/make/modules/jdk.jconsole/Launcher.gmk b/make/modules/jdk.jconsole/Launcher.gmk index 7cb40a1b13a..adb3b05c400 100644 --- a/make/modules/jdk.jconsole/Launcher.gmk +++ b/make/modules/jdk.jconsole/Launcher.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 @@ -30,15 +30,15 @@ include LauncherCommon.gmk ################################################################################ ## Build jconsole ################################################################################ - -$(eval $(call SetupBuildLauncher, jconsole, \ - MAIN_CLASS := sun.tools.jconsole.JConsole, \ - JAVA_ARGS := \ - --add-opens java.base/java.io=jdk.jconsole \ - --add-modules ALL-DEFAULT \ - -Djconsole.showOutputViewer \ - -Djdk.attach.allowAttachSelf=true, \ - WINDOWS_JAVAW := true, \ -)) - +ifneq ($(ENABLE_HEADLESS_ONLY), true) + $(eval $(call SetupBuildLauncher, jconsole, \ + MAIN_CLASS := sun.tools.jconsole.JConsole, \ + JAVA_ARGS := \ + --add-opens java.base/java.io=jdk.jconsole \ + --add-modules ALL-DEFAULT \ + -Djconsole.showOutputViewer \ + -Djdk.attach.allowAttachSelf=true, \ + WINDOWS_JAVAW := true, \ + )) +endif ################################################################################ diff --git a/make/modules/jdk.management/Lib.gmk b/make/modules/jdk.management/Lib.gmk index 8991414b44e..f65348e9381 100644 --- a/make/modules/jdk.management/Lib.gmk +++ b/make/modules/jdk.management/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 @@ -41,8 +41,6 @@ endif $(eval $(call SetupJdkLibrary, BUILD_LIBMANAGEMENT_EXT, \ NAME := management_ext, \ OPTIMIZATION := HIGH, \ - DISABLED_WARNINGS_gcc_DiagnosticCommandImpl.c := unused-variable, \ - DISABLED_WARNINGS_clang_DiagnosticCommandImpl.c := unused-variable, \ DISABLED_WARNINGS_clang_UnixOperatingSystem.c := format-nonliteral, \ CFLAGS := $(LIBMANAGEMENT_EXT_CFLAGS), \ JDK_LIBS := java.base:libjava java.base:libjvm, \ diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index 207f82241aa..a3426ffd264 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.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 @@ -69,6 +69,9 @@ ifeq ($(call isTargetOs, linux), true) HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ldl HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libfast-math := -ffast-math + ifeq ($(call isTargetCpuBits, 32), true) + HOTSPOT_JTREG_LIBRARIES_LIBS_libSuspendInCritical += -latomic + endif else HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c \ exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index f31514e666c..37c8e0ae011 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1198,8 +1198,12 @@ class HandlerImpl { static int emit_deopt_handler(C2_MacroAssembler* masm); static uint size_deopt_handler() { - // count one branch instruction and one far call instruction sequence - return NativeInstruction::instruction_size + MacroAssembler::far_codestub_branch_size(); + bool use_far_branch = MacroAssembler::target_needs_far_branch(SharedRuntime::deopt_blob()->unpack()); + // far: adrp, add, blr; near: bl + uint target_branch_instructions = use_far_branch ? 3 : 1; + // target branch + one branch instruction + uint deopt_handler_instructions = target_branch_instructions + 1; + return deopt_handler_instructions * NativeInstruction::instruction_size; } }; @@ -2512,25 +2516,25 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -const RegMask& Matcher::divI_proj_mask() { +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } @@ -8224,7 +8228,7 @@ instruct encodeKlass_not_null(iRegNNoSp dst, iRegP src) %{ ins_encode %{ Register src_reg = as_Register($src$$reg); Register dst_reg = as_Register($dst$$reg); - __ encode_klass_not_null(dst_reg, src_reg); + __ encode_klass_not_null(dst_reg, src_reg, rscratch1); %} ins_pipe(ialu_reg); @@ -8239,11 +8243,7 @@ instruct decodeKlass_not_null(iRegPNoSp dst, iRegN src) %{ ins_encode %{ Register src_reg = as_Register($src$$reg); Register dst_reg = as_Register($dst$$reg); - if (dst_reg != src_reg) { - __ decode_klass_not_null(dst_reg, src_reg); - } else { - __ decode_klass_not_null(dst_reg); - } + __ decode_klass_not_null(dst_reg, src_reg, rscratch1); %} ins_pipe(ialu_reg); @@ -8294,6 +8294,34 @@ instruct castII_checked(iRegI dst, rFlagsReg cr) ins_pipe(pipe_slow); %} +// The unchecked and checked variants for CastII below both use iRegINoSp for src and dst +// as some consumers of CastII node like ConvHF2F forbid the stack pointer as an input +// (please see convHF2F_reg_reg rule which requires input to be in an iRegINoSp register). +instruct castII_nosp(iRegINoSp dst) +%{ + predicate(VerifyConstraintCasts == 0); + match(Set dst (CastII dst)); + + size(0); + format %{ "# castII of $dst" %} + ins_encode(/* empty encoding */); + ins_cost(0); + ins_pipe(pipe_class_empty); +%} + +instruct castII_checked_nosp(iRegINoSp dst, rFlagsReg cr) +%{ + predicate(VerifyConstraintCasts > 0); + match(Set dst (CastII dst)); + effect(KILL cr); + + format %{ "# castII_checked of $dst" %} + ins_encode %{ + __ verify_int_in_range(_idx, bottom_type()->is_int(), $dst$$Register, rscratch1); + %} + ins_pipe(pipe_slow); +%} + instruct castLL(iRegL dst) %{ predicate(VerifyConstraintCasts == 0); diff --git a/src/hotspot/cpu/aarch64/aarch64_atomic.ad b/src/hotspot/cpu/aarch64/aarch64_atomic.ad index 3b05a637215..13fbe781518 100644 --- a/src/hotspot/cpu/aarch64/aarch64_atomic.ad +++ b/src/hotspot/cpu/aarch64/aarch64_atomic.ad @@ -43,8 +43,7 @@ instruct compareAndExchangeB(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::byte, memory_order_release, $res$$Register); __ sxtbw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -59,8 +58,7 @@ instruct compareAndExchangeS(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::halfword, memory_order_release, $res$$Register); __ sxthw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -75,8 +73,7 @@ instruct compareAndExchangeI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -90,8 +87,7 @@ instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -106,8 +102,7 @@ instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -122,8 +117,7 @@ instruct compareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -138,8 +132,7 @@ instruct compareAndExchangeBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::byte, memory_order_seq_cst, $res$$Register); __ sxtbw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -155,8 +148,7 @@ instruct compareAndExchangeSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::halfword, memory_order_seq_cst, $res$$Register); __ sxthw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -172,8 +164,7 @@ instruct compareAndExchangeIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -188,8 +179,7 @@ instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -204,8 +194,7 @@ instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -220,8 +209,7 @@ instruct compareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -235,9 +223,7 @@ instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -252,9 +238,7 @@ instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -269,9 +253,7 @@ instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -286,9 +268,7 @@ instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -304,9 +284,7 @@ instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -322,9 +300,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -340,9 +316,7 @@ instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -358,9 +332,7 @@ instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -376,9 +348,7 @@ instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -394,9 +364,7 @@ instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -412,9 +380,7 @@ instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -430,9 +396,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -447,9 +411,7 @@ instruct weakCompareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -464,9 +426,7 @@ instruct weakCompareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -481,9 +441,7 @@ instruct weakCompareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -498,9 +456,7 @@ instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -516,9 +472,7 @@ instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -534,9 +488,7 @@ instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -552,9 +504,7 @@ instruct weakCompareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -570,9 +520,7 @@ instruct weakCompareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -588,9 +536,7 @@ instruct weakCompareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -606,9 +552,7 @@ instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -624,9 +568,7 @@ instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -642,9 +584,7 @@ instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 index dc51754e7f9..d6b3abd1e6f 100644 --- a/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 @@ -53,8 +53,7 @@ ifelse($7,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));),`dnl') %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($7,Acq,true,false), /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::$4, ifelse($7,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); __ $6($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -76,8 +75,7 @@ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_Lo %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); %} ins_pipe(pipe_slow); %}')dnl @@ -112,9 +110,7 @@ ifelse($6,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));),`dnl') "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ ifelse($7,Weak,true,false), noreg); + __ ifelse($7,Weak,cmpxchg_weak,cmpxchg)($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release)); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -137,9 +133,7 @@ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_Lo "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ ifelse($7,Weak,true,false), noreg); + __ ifelse($7,Weak,cmpxchg_weak,cmpxchg)($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release)); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index b9899995531..c06c8b856b7 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -251,9 +251,9 @@ source %{ 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. + // AddReductionVHF and MulReductionVHF are currently only produced by the + // auto-vectorizer (the Vector API does not yet intrinsify Float16 reductions), + // 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 @@ -317,6 +317,13 @@ source %{ return false; // NEON only, since SLI/USHR are not available in SVE } break; + case Op_VectorBitwiseBlend: + // Use NEON BSL when UseSVE < 2; SVE1 has no BSL so larger vectors are + // not supported on UseSVE == 1 machines. + if (UseSVE < 2 && length_in_bytes > 16) { + return false; + } + break; default: break; } @@ -340,6 +347,7 @@ source %{ case Op_MulReductionVL: case Op_CompressBitsV: case Op_ExpandBitsV: + case Op_VectorBitwiseBlend: return false; case Op_SaturatingAddV: case Op_SaturatingSubV: @@ -364,9 +372,9 @@ source %{ opcode = Op_StoreVectorScatterMasked; break; // Currently, the masked versions of the following 8 Float16 operations are disabled. - // When the support for Float16 vector classes is added in VectorAPI and the masked - // Float16 IR can be generated, these masked operations will be enabled and relevant - // backend support added. + // The Vector API does not yet emit predicated Float16 IR. When such masked IR can be + // generated, these masked operations will be enabled and the relevant backend support + // added. case Op_AddVHF: case Op_SubVHF: case Op_MulVHF: @@ -1149,7 +1157,8 @@ instruct vmulI_sve(vReg dst_src1, vReg src2) %{ // vector mul - LONG instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ - predicate(UseSVE == 0); + predicate(UseSVE == 0 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs()); match(Set dst (MulVL src1 src2)); format %{ "vmulL_neon $dst, $src1, $src2\t# 2L" %} ins_encode %{ @@ -1167,8 +1176,75 @@ instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ ins_pipe(pipe_slow); %} +// Specialization of vmulL_int_neon when both inputs are the same IR node +// (e.g. v * v). Avoids one redundant xtn and saves one temporary register. +instruct vmulL_int_neon_same(vReg dst, vReg src, vReg tmp) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_int_inputs() && + n->in(1) == n->in(2)); + match(Set dst (MulVL src src)); + effect(TEMP tmp); + format %{ "vmulL_int_neon_same $dst, $src, $src\t# 2L. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp$$FloatRegister, __ T2S, $src$$FloatRegister, __ T2D); + __ smullv($dst$$FloatRegister, __ T2S, $tmp$$FloatRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_int_neon(vReg dst, vReg src1, vReg src2, vReg tmp1, vReg tmp2) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_int_inputs() && + n->in(1) != n->in(2)); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp2); + format %{ "vmulL_int_neon $dst, $src1, $src2\t# 2L. KILL $tmp1, $tmp2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp1$$FloatRegister, __ T2S, $src1$$FloatRegister, __ T2D); + __ xtn($tmp2$$FloatRegister, __ T2S, $src2$$FloatRegister, __ T2D); + __ smullv($dst$$FloatRegister, __ T2S, $tmp1$$FloatRegister, $tmp2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// Specialization of vmulL_uint_neon when both inputs are the same IR node +// (e.g. v * v). Avoids one redundant xtn and saves one temporary register. +instruct vmulL_uint_neon_same(vReg dst, vReg src, vReg tmp) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_uint_inputs() && + n->in(1) == n->in(2)); + match(Set dst (MulVL src src)); + effect(TEMP tmp); + format %{ "vmulL_uint_neon_same $dst, $src, $src\t# 2L. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp$$FloatRegister, __ T2S, $src$$FloatRegister, __ T2D); + __ umullv($dst$$FloatRegister, __ T2S, $tmp$$FloatRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_uint_neon(vReg dst, vReg src1, vReg src2, vReg tmp1, vReg tmp2) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_uint_inputs() && + n->in(1) != n->in(2)); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp2); + format %{ "vmulL_uint_neon $dst, $src1, $src2\t# 2L. KILL $tmp1, $tmp2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp1$$FloatRegister, __ T2S, $src1$$FloatRegister, __ T2D); + __ xtn($tmp2$$FloatRegister, __ T2S, $src2$$FloatRegister, __ T2D); + __ umullv($dst$$FloatRegister, __ T2S, $tmp1$$FloatRegister, $tmp2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + instruct vmulL_sve(vReg dst_src1, vReg src2) %{ - predicate(UseSVE > 0); + predicate(UseSVE == 1 || (UseSVE == 2 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs())); match(Set dst_src1 (MulVL dst_src1 src2)); format %{ "vmulL_sve $dst_src1, $dst_src1, $src2" %} ins_encode %{ @@ -1177,6 +1253,26 @@ instruct vmulL_sve(vReg dst_src1, vReg src2) %{ ins_pipe(pipe_slow); %} +instruct vmulL_int_sve2(vReg dst, vReg src1, vReg src2) %{ + predicate(UseSVE == 2 && n->as_MulVL()->has_int_inputs()); + match(Set dst (MulVL src1 src2)); + format %{ "vmulL_int_sve2 $dst, $src1, $src2" %} + ins_encode %{ + __ sve_smullb($dst$$FloatRegister, __ D, $src1$$FloatRegister, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_uint_sve2(vReg dst, vReg src1, vReg src2) %{ + predicate(UseSVE == 2 && n->as_MulVL()->has_uint_inputs()); + match(Set dst (MulVL src1 src2)); + format %{ "vmulL_uint_sve2 $dst, $src1, $src2" %} + ins_encode %{ + __ sve_umullb($dst$$FloatRegister, __ D, $src1$$FloatRegister, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // vector mul - floating-point instruct vmulHF(vReg dst, vReg src1, vReg src2) %{ @@ -7051,6 +7147,31 @@ instruct vblend_sve(vReg dst, vReg src1, vReg src2, pReg pg) %{ ins_pipe(pipe_slow); %} +// ------------------------------ Vector bitwise blend ------------------------- + +instruct vbitwise_blend_neon_sve1(vReg src1, vReg src2, vReg dst_src3) %{ + predicate(UseSVE < 2 && + VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n))); + match(Set dst_src3 (VectorBitwiseBlend (Binary src1 src2) dst_src3)); + format %{ "vbitwise_blend_neon_sve1 $src1, $src2, $dst_src3" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + Assembler::SIMD_Arrangement T = length_in_bytes == 16 ? __ T16B : __ T8B; + __ bsl($dst_src3$$FloatRegister, T, $src2$$FloatRegister, $src1$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vbitwise_blend_sve2(vReg src1, vReg dst_src2, vReg src3) %{ + predicate(UseSVE == 2); + match(Set dst_src2 (VectorBitwiseBlend (Binary src1 dst_src2) src3)); + format %{ "vbitwise_blend_sve2 $src1, $dst_src2, $src3" %} + ins_encode %{ + __ sve_bsl($dst_src2$$FloatRegister, $src1$$FloatRegister, $src3$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // ------------------------------ Vector round --------------------------------- // vector Math.round diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index a53efd43d5d..b749647ae1e 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -241,9 +241,9 @@ source %{ 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. + // AddReductionVHF and MulReductionVHF are currently only produced by the + // auto-vectorizer (the Vector API does not yet intrinsify Float16 reductions), + // 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 @@ -307,6 +307,13 @@ source %{ return false; // NEON only, since SLI/USHR are not available in SVE } break; + case Op_VectorBitwiseBlend: + // Use NEON BSL when UseSVE < 2; SVE1 has no BSL so larger vectors are + // not supported on UseSVE == 1 machines. + if (UseSVE < 2 && length_in_bytes > 16) { + return false; + } + break; default: break; } @@ -330,6 +337,7 @@ source %{ case Op_MulReductionVL: case Op_CompressBitsV: case Op_ExpandBitsV: + case Op_VectorBitwiseBlend: return false; case Op_SaturatingAddV: case Op_SaturatingSubV: @@ -354,9 +362,9 @@ source %{ opcode = Op_StoreVectorScatterMasked; break; // Currently, the masked versions of the following 8 Float16 operations are disabled. - // When the support for Float16 vector classes is added in VectorAPI and the masked - // Float16 IR can be generated, these masked operations will be enabled and relevant - // backend support added. + // The Vector API does not yet emit predicated Float16 IR. When such masked IR can be + // generated, these masked operations will be enabled and the relevant backend support + // added. case Op_AddVHF: case Op_SubVHF: case Op_MulVHF: @@ -728,7 +736,8 @@ BINARY_OP_NEON_SVE_PAIRWISE(vmulI, MulVI, mulv, sve_mul, S) // vector mul - LONG instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ - predicate(UseSVE == 0); + predicate(UseSVE == 0 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs()); match(Set dst (MulVL src1 src2)); format %{ "vmulL_neon $dst, $src1, $src2\t# 2L" %} ins_encode %{ @@ -746,8 +755,47 @@ instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ ins_pipe(pipe_slow); %} +dnl VMUL_L_NEON($1, $2 ) +dnl VMUL_L_NEON(kind, insn ) +define(`VMUL_L_NEON', `dnl +// Specialization of vmulL_$1_neon when both inputs are the same IR node +// (e.g. v * v). Avoids one redundant xtn and saves one temporary register. +instruct vmulL_$1_neon_same(vReg dst, vReg src, vReg tmp) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_$1_inputs() && + n->in(1) == n->in(2)); + match(Set dst (MulVL src src)); + effect(TEMP tmp); + format %{ "vmulL_$1_neon_same $dst, $src, $src\t# 2L. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp$$FloatRegister, __ T2S, $src$$FloatRegister, __ T2D); + __ $2($dst$$FloatRegister, __ T2S, $tmp$$FloatRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_$1_neon(vReg dst, vReg src1, vReg src2, vReg tmp1, vReg tmp2) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_$1_inputs() && + n->in(1) != n->in(2)); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp2); + format %{ "vmulL_$1_neon $dst, $src1, $src2\t# 2L. KILL $tmp1, $tmp2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp1$$FloatRegister, __ T2S, $src1$$FloatRegister, __ T2D); + __ xtn($tmp2$$FloatRegister, __ T2S, $src2$$FloatRegister, __ T2D); + __ $2($dst$$FloatRegister, __ T2S, $tmp1$$FloatRegister, $tmp2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} +')dnl +VMUL_L_NEON(int, smullv) +VMUL_L_NEON(uint, umullv) instruct vmulL_sve(vReg dst_src1, vReg src2) %{ - predicate(UseSVE > 0); + predicate(UseSVE == 1 || (UseSVE == 2 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs())); match(Set dst_src1 (MulVL dst_src1 src2)); format %{ "vmulL_sve $dst_src1, $dst_src1, $src2" %} ins_encode %{ @@ -756,6 +804,21 @@ instruct vmulL_sve(vReg dst_src1, vReg src2) %{ ins_pipe(pipe_slow); %} +dnl VMUL_L_SVE2($1, $2 ) +dnl VMUL_L_SVE2(kind, sve2_insn ) +define(`VMUL_L_SVE2', `dnl +instruct vmulL_$1_sve2(vReg dst, vReg src1, vReg src2) %{ + predicate(UseSVE == 2 && n->as_MulVL()->has_$1_inputs()); + match(Set dst (MulVL src1 src2)); + format %{ "vmulL_$1_sve2 $dst, $src1, $src2" %} + ins_encode %{ + __ $2($dst$$FloatRegister, __ D, $src1$$FloatRegister, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} +')dnl +VMUL_L_SVE2(int, sve_smullb) +VMUL_L_SVE2(uint, sve_umullb) // vector mul - floating-point BINARY_OP(vmulHF, MulVHF, fmul, sve_fmul, H) BINARY_OP(vmulF, MulVF, fmul, sve_fmul, S) @@ -4754,6 +4817,31 @@ instruct vblend_sve(vReg dst, vReg src1, vReg src2, pReg pg) %{ ins_pipe(pipe_slow); %} +// ------------------------------ Vector bitwise blend ------------------------- + +instruct vbitwise_blend_neon_sve1(vReg src1, vReg src2, vReg dst_src3) %{ + predicate(UseSVE < 2 && + VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n))); + match(Set dst_src3 (VectorBitwiseBlend (Binary src1 src2) dst_src3)); + format %{ "vbitwise_blend_neon_sve1 $src1, $src2, $dst_src3" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + Assembler::SIMD_Arrangement T = length_in_bytes == 16 ? __ T16B : __ T8B; + __ bsl($dst_src3$$FloatRegister, T, $src2$$FloatRegister, $src1$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vbitwise_blend_sve2(vReg src1, vReg dst_src2, vReg src3) %{ + predicate(UseSVE == 2); + match(Set dst_src2 (VectorBitwiseBlend (Binary src1 dst_src2) src3)); + format %{ "vbitwise_blend_sve2 $src1, $dst_src2, $src3" %} + ins_encode %{ + __ sve_bsl($dst_src2$$FloatRegister, $src1$$FloatRegister, $src3$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // ------------------------------ Vector round --------------------------------- // vector Math.round diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index c8d5ee2eaeb..a81213c5ae4 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -3151,6 +3151,34 @@ public: _pmull(Vd, Ta, Vn, Vm, Tb); } + //Vector by element variant of UMULL + void _umullv(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, + SIMD_Arrangement Tb, FloatRegister Vm, SIMD_RegVariant Ts, int lane) { + starti; + int size = (Ta == T4S) ? 0b01 : 0b10; + int q = (Tb == T4H || Tb == T2S) ? 0 : 1; + int h = (size == 0b01) ? ((lane >> 2) & 1) : ((lane >> 1) & 1); + int l = (size == 0b01) ? ((lane >> 1) & 1) : (lane & 1); + assert(Ta == T4S || Ta == T2D, "umull{2}v destination register must have arrangement T4S or T2D"); + assert(size == 0b10 ? lane < 4 : lane < 8, "umull{2}v assumes lane < 4 when using half-words and lane < 8 otherwise"); + assert(Ts == H ? Vm->encoding() < 16 : Vm->encoding() < 32, "umull{2}v requires Vm to be in range V0..V15 when Ts is H"); + f(0, 31), f(q, 30), f(0b101111, 29, 24), f(size, 23, 22), f(l, 21); //f(m, 20); + rf(Vm, 16), f(0b1010, 15, 12), f(h, 11), f(0, 10), rf(Vn, 5), rf(Vd, 0); + } + + //Vector by element variant of UMULL + void umullv(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, + SIMD_Arrangement Tb, FloatRegister Vm, SIMD_RegVariant Ts, int lane) { + assert(Ta == T4S ? (Tb == T4H && Ts == H) : (Tb == T2S && Ts == S), "umullv register arrangements must adhere to spec"); + _umullv(Vd, Ta, Vn, Tb, Vm, Ts, lane); + } + + void umull2v(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, + SIMD_Arrangement Tb, FloatRegister Vm, SIMD_RegVariant Ts, int lane) { + assert(Ta == T4S ? (Tb == T8H && Ts == H) : (Tb == T4S && Ts == S), "umull2v register arrangements must adhere to spec"); + _umullv(Vd, Ta, Vn, Tb, Vm, Ts, lane); + } + void uqxtn(FloatRegister Vd, SIMD_Arrangement Tb, FloatRegister Vn, SIMD_Arrangement Ta) { starti; int size_b = (int)Tb >> 1; @@ -4292,14 +4320,31 @@ public: #undef INSN // SVE2 bitwise ternary operations -#define INSN(NAME, opc) \ - void NAME(FloatRegister Zdn, FloatRegister Zm, FloatRegister Zk) { \ - starti; \ - f(0b00000100, 31, 24), f(opc, 23, 21), rf(Zm, 16); \ - f(0b001110, 15, 10), rf(Zk, 5), rf(Zdn, 0); \ +#define INSN(NAME, op1, op2) \ + void NAME(FloatRegister Zdn, FloatRegister Zm, FloatRegister Zk) { \ + starti; \ + f(0b00000100, 31, 24), f(op1, 23, 21), rf(Zm, 16); \ + f(0b00111, 15, 11), f(op2, 10), rf(Zk, 5), rf(Zdn, 0); \ } - INSN(sve_eor3, 0b001); // Bitwise exclusive OR of three vectors + INSN(sve_eor3, 0b001, 0b0); // Bitwise exclusive OR of three vectors + INSN(sve_bsl, 0b001, 0b1); // Bitwise select +#undef INSN + +// SVE2 widening integer multiply - vector +#define INSN(NAME, is_unsigned, is_top) \ + void NAME(FloatRegister Zd, SIMD_RegVariant T, FloatRegister Zn, FloatRegister Zm) { \ + starti; \ + assert(T != B && T != Q, "invalid size"); \ + int op = 0b011100 | (is_unsigned ? 0b10 : 0) | (is_top ? 0b1 : 0); \ + f(0b01000101, 31, 24), f(T, 23, 22), f(0, 21), rf(Zm, 16); \ + f(op, 15, 10), rf(Zn, 5), rf(Zd, 0); \ + } + + INSN(sve_umullb, /* is_unsigned */ true, /* is_top */ false); // Unsigned widening multiply of bottom elements + INSN(sve_umullt, /* is_unsigned */ true, /* is_top */ true ); // Unsigned widening multiply of top elements + INSN(sve_smullb, /* is_unsigned */ false, /* is_top */ false); // Signed widening multiply of bottom elements + INSN(sve_smullt, /* is_unsigned */ false, /* is_top */ true ); // Signed widening multiply of top elements #undef INSN // SVE2 saturating operations - predicate diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 87451b5a07a..5b77d15457f 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -452,16 +452,6 @@ int LIR_Assembler::emit_deopt_handler() { return entry_offset; } -void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { - _masm->code_section()->relocate(adr, relocInfo::poll_type); - int pc_offset = code_offset(); - flush_debug_info(pc_offset); - info->record_debug_info(compilation()->debug_info_recorder(), pc_offset); - if (info->exception_handlers() != nullptr) { - compilation()->add_exception_handlers_for_pco(pc_offset, info->exception_handlers()); - } -} - void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,"); @@ -1334,7 +1324,7 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L __ bind(not_null); Register recv = k_RInfo; - __ load_klass(recv, obj); + __ load_klass(recv, obj, rscratch1); type_profile_helper(mdo, md, data, recv); } else { __ cbz(obj, *obj_is_null); @@ -1350,15 +1340,15 @@ 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 - __ load_klass(rscratch1, obj); - __ cmp( rscratch1, k_RInfo); + __ load_klass(rscratch2, obj, rscratch1); + __ cmp( rscratch2, k_RInfo); __ br(Assembler::NE, *failure_target); // successful cast, fall through to profile or jump } else { // get object class // not a safepoint as obj null check happens earlier - __ load_klass(klass_RInfo, obj); + __ load_klass(klass_RInfo, obj, rscratch1); if (k->is_loaded()) { // See if we get an immediate positive hit __ ldr(rscratch1, Address(klass_RInfo, int64_t(k->super_check_offset()))); @@ -1443,15 +1433,15 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { __ bind(not_null); Register recv = k_RInfo; - __ load_klass(recv, value); + __ load_klass(recv, value, rscratch1); type_profile_helper(mdo, md, data, recv); } else { __ cbz(value, done); } add_debug_info_for_null_check_here(op->info_for_exception()); - __ load_klass(k_RInfo, array); - __ load_klass(klass_RInfo, value); + __ load_klass(k_RInfo, array, rscratch1); + __ load_klass(klass_RInfo, value, rscratch1); // get instance klass (it's already uncompressed) __ ldr(k_RInfo, Address(k_RInfo, ObjArrayKlass::element_klass_offset())); @@ -1492,12 +1482,12 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { } void LIR_Assembler::casw(Register addr, Register newval, Register cmpval) { - __ cmpxchg(addr, cmpval, newval, Assembler::word, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1); + __ cmpxchg(addr, cmpval, newval, Assembler::word, memory_order_seq_cst, rscratch1); __ cset(rscratch1, Assembler::NE); } void LIR_Assembler::casl(Register addr, Register newval, Register cmpval) { - __ cmpxchg(addr, cmpval, newval, Assembler::xword, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1); + __ cmpxchg(addr, cmpval, newval, Assembler::xword, memory_order_seq_cst, rscratch1); __ cset(rscratch1, Assembler::NE); } @@ -2268,14 +2258,14 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // an instance type. if (flags & LIR_OpArrayCopy::type_check) { if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::dst_objarray)) { - __ load_klass(tmp, dst); + __ load_klass(tmp, dst, rscratch1); __ ldrw(rscratch1, Address(tmp, in_bytes(Klass::layout_helper_offset()))); __ cmpw(rscratch1, Klass::_lh_neutral_value); __ br(Assembler::GE, *stub->entry()); } if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::src_objarray)) { - __ load_klass(tmp, src); + __ load_klass(tmp, src, rscratch1); __ ldrw(rscratch1, Address(tmp, in_bytes(Klass::layout_helper_offset()))); __ cmpw(rscratch1, Klass::_lh_neutral_value); __ br(Assembler::GE, *stub->entry()); @@ -2329,8 +2319,8 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { __ PUSH(src, dst); - __ load_klass(src, src); - __ load_klass(dst, dst); + __ load_klass(src, src, rscratch1); + __ load_klass(dst, dst, rscratch1); __ check_klass_subtype_fast_path(src, dst, tmp, &cont, &slow, nullptr); @@ -2354,9 +2344,9 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { assert(flags & mask, "one of the two should be known to be an object array"); if (!(flags & LIR_OpArrayCopy::src_objarray)) { - __ load_klass(tmp, src); + __ load_klass(tmp, src, rscratch1); } else if (!(flags & LIR_OpArrayCopy::dst_objarray)) { - __ load_klass(tmp, dst); + __ load_klass(tmp, dst, rscratch1); } int lh_offset = in_bytes(Klass::layout_helper_offset()); Address klass_lh_addr(tmp, lh_offset); @@ -2382,7 +2372,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { __ uxtw(c_rarg2, length); assert_different_registers(c_rarg2, dst); - __ load_klass(c_rarg4, dst); + __ load_klass(c_rarg4, dst, rscratch1); __ ldr(c_rarg4, Address(c_rarg4, ObjArrayKlass::element_klass_offset())); __ ldrw(c_rarg3, Address(c_rarg4, Klass::super_check_offset_offset())); __ far_call(RuntimeAddress(copyfunc_addr)); @@ -2438,12 +2428,12 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { __ mov_metadata(tmp, default_type->constant_encoding()); if (basic_type != T_OBJECT) { - __ cmp_klass(dst, tmp, rscratch1); + __ cmp_klass(dst, tmp, rscratch1, rscratch2); __ br(Assembler::NE, halt); - __ cmp_klass(src, tmp, rscratch1); + __ cmp_klass(src, tmp, rscratch1, rscratch2); __ br(Assembler::EQ, known_ok); } else { - __ cmp_klass(dst, tmp, rscratch1); + __ cmp_klass(dst, tmp, rscratch1, rscratch2); __ br(Assembler::EQ, known_ok); __ cmp(src, dst); __ br(Assembler::EQ, known_ok); @@ -2518,7 +2508,7 @@ void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { add_debug_info_for_null_check_here(info); } - __ load_klass(result, obj); + __ load_klass(result, obj, rscratch1); } void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { @@ -2560,7 +2550,7 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { // Fall back to runtime helper to handle the rest at runtime. __ mov_metadata(recv, known_klass->constant_encoding()); } else { - __ load_klass(recv, recv); + __ load_klass(recv, recv, rscratch1); } type_profile_helper(mdo, md, data, recv); } else { @@ -2646,7 +2636,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { #ifdef ASSERT if (exact_klass != nullptr) { Label ok; - __ load_klass(tmp, tmp); + __ load_klass(tmp, tmp, rscratch1); __ mov_metadata(rscratch1, exact_klass->constant_encoding()); __ eor(rscratch1, tmp, rscratch1); __ cbz(rscratch1, ok); @@ -2659,7 +2649,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { if (exact_klass != nullptr) { __ mov_metadata(tmp, exact_klass->constant_encoding()); } else { - __ load_klass(tmp, tmp); + __ load_klass(tmp, tmp, rscratch1); } __ ldr(rscratch2, mdo_addr); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 367256d2f69..bebc9543b40 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -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, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -52,7 +52,6 @@ friend class ArrayCopyStub; // Record the type of the receiver in ReceiverTypeData void type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, Register recv); - void add_debug_info_for_branch(address adr, CodeEmitInfo* info); void casw(Register addr, Register newval, Register cmpval); void casl(Register addr, Register newval, Register cmpval); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index 89a9422ea48..f81c976d291 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -105,7 +105,7 @@ 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())); - encode_klass_not_null(t1, klass); // Take care not to kill klass + encode_klass_not_null(t1, klass, t1); // Take care not to kill klass strw(t1, Address(obj, oopDesc::klass_offset_in_bytes())); } diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp index 449ad4f8a4c..1745bb8aae9 100644 --- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp @@ -824,7 +824,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { // load the klass and check the has finalizer flag Label register_finalizer; Register t = r5; - __ load_klass(t, r0); + __ load_klass(t, r0, rscratch1); __ ldrb(t, Address(t, Klass::misc_flags_offset())); __ tbnz(t, exact_log2(KlassFlags::_misc_has_finalizer), register_finalizer); __ ret(lr); @@ -947,7 +947,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { __ br(Assembler::EQ, is_secondary); // Klass is a secondary superclass // Klass is a concrete class - __ load_klass(r5, obj); + __ load_klass(r5, obj, rscratch1); __ ldr(rscratch1, Address(r5, r3)); __ cmp(klass, rscratch1); __ cset(result, Assembler::EQ); @@ -955,7 +955,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { __ bind(is_secondary); - __ load_klass(obj, obj); + __ load_klass(obj, obj, rscratch1); // This is necessary because I am never in my own secondary_super list. __ cmp(obj, klass); diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index cb9e308197e..3321fcc4edb 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -167,7 +167,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, } if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(t1, obj); + load_klass(t1, obj, rscratch2); ldrb(t1, Address(t1, Klass::misc_flags_offset())); tst(t1, KlassFlags::_misc_is_value_based_class); br(Assembler::NE, slow_path); @@ -204,8 +204,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Try to lock. Transition lock-bits 0b01 => 0b00 orr(t1_mark, t1_mark, markWord::unlocked_value); eor(t3_t, t1_mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, - /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, memory_order_acquire); br(Assembler::NE, slow_path); bind(push); @@ -285,8 +284,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Try to CAS owner (no owner => current thread's _monitor_owner_id). ldr(rscratch2, Address(rthread, JavaThread::monitor_owner_id_offset())); - cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, /*acquire*/ true, - /*release*/ false, /*weak*/ false, t3_owner); + cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, memory_order_acquire, t3_owner); br(Assembler::EQ, monitor_locked); // Check if recursive. @@ -371,8 +369,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1, // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(t3_t, t1_mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, - /*acquire*/ false, /*release*/ true, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, memory_order_release); br(Assembler::EQ, unlocked); bind(push_and_slow_path); @@ -1809,19 +1806,19 @@ void C2_MacroAssembler::neon_reduce_mul_integral(Register dst, BasicType bt, if (isQ) { // Multiply the lower half and higher half of vector iteratively. // vtmp1 = vsrc[8:15] - ins(vtmp1, D, vsrc, 0, 1); + ext(vtmp1, T16B, vsrc, vsrc, 8); // vtmp1[n] = vsrc[n] * vsrc[n + 8], where n=[0, 7] mulv(vtmp1, T8B, vtmp1, vsrc); // vtmp2 = vtmp1[4:7] - ins(vtmp2, S, vtmp1, 0, 1); + ext(vtmp2, T8B, vtmp1, vtmp1, 4); // vtmp1[n] = vtmp1[n] * vtmp1[n + 4], where n=[0, 3] mulv(vtmp1, T8B, vtmp2, vtmp1); } else { - ins(vtmp1, S, vsrc, 0, 1); + ext(vtmp1, T8B, vsrc, vsrc, 4); mulv(vtmp1, T8B, vtmp1, vsrc); } // vtmp2 = vtmp1[2:3] - ins(vtmp2, H, vtmp1, 0, 1); + ext(vtmp2, T8B, vtmp1, vtmp1, 2); // vtmp2[n] = vtmp1[n] * vtmp1[n + 2], where n=[0, 1] mulv(vtmp2, T8B, vtmp2, vtmp1); // dst = vtmp2[0] * isrc * vtmp2[1] @@ -1834,12 +1831,12 @@ void C2_MacroAssembler::neon_reduce_mul_integral(Register dst, BasicType bt, break; case T_SHORT: if (isQ) { - ins(vtmp2, D, vsrc, 0, 1); + ext(vtmp2, T16B, vsrc, vsrc, 8); mulv(vtmp2, T4H, vtmp2, vsrc); - ins(vtmp1, S, vtmp2, 0, 1); + ext(vtmp1, T8B, vtmp2, vtmp2, 4); mulv(vtmp1, T4H, vtmp1, vtmp2); } else { - ins(vtmp1, S, vsrc, 0, 1); + ext(vtmp1, T8B, vsrc, vsrc, 4); mulv(vtmp1, T4H, vtmp1, vsrc); } umov(rscratch1, vtmp1, H, 0); @@ -1851,7 +1848,7 @@ void C2_MacroAssembler::neon_reduce_mul_integral(Register dst, BasicType bt, break; case T_INT: if (isQ) { - ins(vtmp1, D, vsrc, 0, 1); + ext(vtmp1, T16B, vsrc, vsrc, 8); mulv(vtmp1, T2S, vtmp1, vsrc); } else { vtmp1 = vsrc; @@ -1907,19 +1904,19 @@ void C2_MacroAssembler::neon_reduce_mul_fp(FloatRegister dst, BasicType bt, break; case T_FLOAT: fmuls(dst, fsrc, vsrc); - ins(vtmp, S, vsrc, 0, 1); + ext(vtmp, T8B, vsrc, vsrc, 4); fmuls(dst, dst, vtmp); if (isQ) { - ins(vtmp, S, vsrc, 0, 2); + ext(vtmp, T16B, vsrc, vsrc, 8); fmuls(dst, dst, vtmp); - ins(vtmp, S, vsrc, 0, 3); + ext(vtmp, T16B, vsrc, vsrc, 12); fmuls(dst, dst, vtmp); } break; case T_DOUBLE: assert(isQ, "unsupported"); fmuld(dst, fsrc, vsrc); - ins(vtmp, D, vsrc, 0, 1); + ext(vtmp, T16B, vsrc, vsrc, 8); fmuld(dst, dst, vtmp); break; default: @@ -2731,7 +2728,8 @@ void C2_MacroAssembler::reconstruct_frame_pointer(Register rtmp) { void C2_MacroAssembler::select_from_two_vectors_neon(FloatRegister dst, FloatRegister src1, FloatRegister src2, FloatRegister index, FloatRegister tmp, unsigned vector_length_in_bytes) { - assert_different_registers(dst, src1, src2, tmp); + assert_different_registers(src2, tmp); + assert_different_registers(index, tmp); SIMD_Arrangement size = vector_length_in_bytes == 16 ? T16B : T8B; if (vector_length_in_bytes == 16) { @@ -2760,7 +2758,8 @@ void C2_MacroAssembler::select_from_two_vectors_sve(FloatRegister dst, FloatRegi FloatRegister src2, FloatRegister index, FloatRegister tmp, SIMD_RegVariant T, unsigned vector_length_in_bytes) { - assert_different_registers(dst, src1, src2, index, tmp); + assert_different_registers(src2, tmp); + assert_different_registers(index, tmp); if (vector_length_in_bytes == 8) { // We need to fit both the source vectors (src1, src2) in a single vector register because the @@ -2787,7 +2786,8 @@ void C2_MacroAssembler::select_from_two_vectors(FloatRegister dst, FloatRegister FloatRegister tmp, BasicType bt, unsigned vector_length_in_bytes) { - assert_different_registers(dst, src1, src2, index, tmp); + assert_different_registers(dst, src1, src2, tmp); + assert_different_registers(index, tmp); // The cases that can reach this method are - // - UseSVE = 0/1, vector_length_in_bytes = 8 or 16, excluding double and long types diff --git a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp index 3874c8cd54e..7cc2a004c40 100644 --- a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp @@ -120,11 +120,7 @@ char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size return result; } -bool CompressedKlassPointers::check_klass_decode_mode(address base, int shift, const size_t range) { - return MacroAssembler::check_klass_decode_mode(base, shift, range); -} - -bool CompressedKlassPointers::set_klass_decode_mode() { +void CompressedKlassPointers::initialize_pd() { const size_t range = klass_range_end() - base(); - return MacroAssembler::set_klass_decode_mode(_base, _shift, range); + MacroAssembler::initialize_klass_decode_mode(_base, _shift, range); } diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 231710df7d7..55a6dde38f5 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.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, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -66,7 +66,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, @@ -187,8 +186,6 @@ // deoptimization support void interpreter_frame_set_last_sp(intptr_t* sp); - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad index 18fc27a4af4..375a0a89760 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad @@ -283,7 +283,7 @@ instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -316,7 +316,7 @@ instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRe RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -346,7 +346,7 @@ instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -377,7 +377,7 @@ instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRe RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -409,8 +409,7 @@ instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -442,8 +441,7 @@ instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNo $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -475,8 +473,7 @@ instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, @@ -509,8 +506,7 @@ instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNo $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 index 8fb1f7e8e42..63b464ceb8c 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 @@ -151,7 +151,7 @@ instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iReg RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + ifelse($1,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -160,8 +160,8 @@ instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iReg %} ins_pipe(pipe_slow); %}')dnl -CAEP_INSN(,,false) -CAEP_INSN(Acq,_acq,true) +CAEP_INSN(,) +CAEP_INSN(Acq,_acq) dnl define(`CAEN_INSN', ` @@ -185,7 +185,7 @@ instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iReg RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + ifelse($1,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -195,8 +195,8 @@ instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iReg %} ins_pipe(pipe_slow); %}')dnl -CAEN_INSN(,,false) -CAEN_INSN(Acq,_acq,true) +CAEN_INSN(,) +CAEN_INSN(Acq,_acq) dnl define(`CASP_INSN', ` @@ -221,8 +221,7 @@ instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoS $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, ifelse($1,Acq,memory_order_seq_cst,memory_order_release)); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -232,8 +231,8 @@ instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoS %} ins_pipe(pipe_slow); %}')dnl -CASP_INSN(,,false) -CASP_INSN(Acq,_acq,true) +CASP_INSN(,) +CASP_INSN(Acq,_acq) dnl define(`CASN_INSN', ` @@ -258,8 +257,7 @@ instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoS $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, ifelse($1,Acq,memory_order_seq_cst,memory_order_release)); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, @@ -270,8 +268,8 @@ instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoS %} ins_pipe(pipe_slow); %}')dnl -CASN_INSN(,,false) -CASN_INSN(Acq,_acq,true) +CASN_INSN(,) +CASN_INSN(Acq,_acq) dnl define(`XCHGP_INSN', ` diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index 38efcf80650..93781bb14bf 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -389,8 +389,8 @@ void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register __ cbnz(tmp1, error); // make sure klass is 'reasonable', which is not zero. - __ load_klass(obj, obj); // get klass - __ cbz(obj, error); // if klass is null it is broken + __ load_narrow_klass(tmp1, obj); // get klass + __ cbz(tmp1, error); // if klass is null it is broken } void BarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path) { diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp index bb93cd9a9d6..89f33cf4529 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp @@ -124,14 +124,13 @@ public: } }; -// The first instruction of the nmethod entry barrier is an ldr (literal) +// The first instruction of the nmethod entry barrier is an ldrw (literal) // instruction. Verify that it's really there, so the offsets are not skewed. bool NativeNMethodBarrier::check_barrier(err_msg& msg) const { - uint32_t* addr = (uint32_t*) instruction_address(); - uint32_t inst = *addr; - if ((inst & 0xff000000) != 0x18000000) { - msg.print("Nmethod entry barrier did not start with ldr (literal) as expected. " - "Addr: " PTR_FORMAT " Code: " UINT32_FORMAT, p2i(addr), inst); + NativeInstruction* ni = nativeInstruction_at(instruction_address()); + if (!ni->is_ldrw_gpr_literal()) { + msg.print("Nmethod entry barrier did not start with ldrw (literal) as expected. " + "Addr: " PTR_FORMAT " Code: " UINT32_FORMAT, p2i(instruction_address()), ni->encoding()); return false; } return true; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index c590b6699c0..19c82ed77ef 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -420,6 +420,32 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ bind(done); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) { + // Check if the oop is in the right area of memory + __ mov(tmp2, (intptr_t) Universe::verify_oop_mask()); + __ andr(tmp1, obj, tmp2); + __ mov(tmp2, (intptr_t) Universe::verify_oop_bits()); + + // Compare tmp1 and tmp2. We don't use a compare + // instruction here because the flags register is live. + __ eor(tmp1, tmp1, tmp2); + __ cbnz(tmp1, L_error); + + // This routine is sometimes called before applying GC barriers. + // With +COH, loading the klass may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); + __ ldrb(tmp1, gc_state); + __ tbnz(tmp1, ShenandoahHeap::HAS_FORWARDED_BITPOS, L_skip); + } + + // Make sure klass is 'reasonable', which is not zero. + __ load_narrow_klass(tmp1, obj); + __ cbz(tmp1, L_error); + __ bind(L_skip); +} + void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register start, Register count, Register scratch) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); @@ -456,79 +482,38 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - Register pre_val_reg = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false); } - __ cbz(pre_val_reg, *stub->continuation()); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); + __ cbz(obj, *stub->continuation()); + ce->store_parameter(obj, 0); + __ far_call(RuntimeAddress(bs->keepalive_barrier_stub())); __ b(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == r0, "C1 must know about our slow call result register"); - assert(res == r0, "result must arrive in r0"); - - if (res != obj) { - __ mov(res, obj); - } - - if (is_strong) { - // Check for object in cset. - if (AOTCodeCache::is_on_for_dump()) { - __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address())); - __ ldr(tmp2, Address(tmp2)); - __ lea(tmp1, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); - __ ldrw(tmp1, Address(tmp1)); - __ lsrv(tmp1, res, tmp1); - } else { - __ mov(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ lsr(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - } - __ ldrb(tmp2, Address(tmp2, tmp1)); - __ cbz(tmp2, *stub->continuation()); - } - - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - if (is_strong) { - if (is_native) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ far_call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mov(obj, slow_result); } __ b(*stub->continuation()); @@ -538,89 +523,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - - // arg0 : previous value of memory - - BarrierSet* bs = BarrierSet::barrier_set(); - - const Register pre_val = r0; - const Register thread = rthread; - const Register tmp = rscratch1; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is marking still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ ldrb(tmp, gc_state); - __ tbz(tmp, ShenandoahHeap::MARKING_BITPOS, done); - - // Can we store original value in the thread's buffer? - __ ldr(tmp, queue_index); - __ cbz(tmp, runtime); - - __ sub(tmp, tmp, wordSize); - __ str(tmp, queue_index); - __ ldr(rscratch2, buffer); - __ add(tmp, tmp, rscratch2); - __ load_parameter(0, rscratch2); - __ str(rscratch2, Address(tmp, 0)); - __ b(done); - - __ bind(runtime); - __ push_call_clobbered_registers(); - __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - __ pop_call_clobbered_registers(); - __ bind(done); - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = r0; + const Register tmp1 = r1; + const Register tmp2 = r2; + __ push(RegSet::of(tmp1, tmp2, tmp_obj), sp); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, rthread, tmp1, tmp2); + __ pop(RegSet::of(tmp1, tmp2, tmp_obj), sp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ push_call_clobbered_registers(); - __ load_parameter(0, r0); - __ load_parameter(1, r1); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - if (is_strong) { - if (is_native) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong))); - } else { - if (UseCompressedOops) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow))); - } else { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong))); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow))); - } else { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak))); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom))); - } - __ blr(lr); - __ mov(rscratch1, r0); - __ pop_call_clobbered_registers(); - __ mov(r0, rscratch1); - + const Register tmp_obj = r0; + const Register tmp_addr = r1; + __ push(RegSet::of(tmp_addr), sp); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop(RegSet::of(tmp_addr), sp); __ epilogue(); } @@ -700,8 +623,14 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp1, tmp2, tmp3, narrow); + atomic_memory_order order = acquire ? memory_order_seq_cst : memory_order_release; + // CAS! - __ cmpxchg(addr, oldval, newval, op_size, acquire, /* release */ true, weak, exchange ? res : noreg); + if (weak) { + __ cmpxchg_weak(addr, oldval, newval, op_size, order, exchange ? res : noreg); + } else { + __ cmpxchg(addr, oldval, newval, op_size, order, exchange ? res : noreg); + } // If we need a boolean result out of CAS, set the flag appropriately and promote the result. if (!exchange) { diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index bab4fb3b37a..62273a44da2 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -32,7 +32,7 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -74,12 +74,14 @@ public: Register obj, Register tmp, Label& slowpath); virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 1eb96cdb6e7..f07f899e869 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -283,10 +283,7 @@ void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm, // If we get this far, we know there is a young raw null value in the field. __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeMov); __ movzw(rtmp1, barrier_Relocation::unpatched); - __ cmpxchg(rtmp2, zr, rtmp1, - Assembler::xword, - false /* acquire */, false /* release */, true /* weak */, - rtmp3); + __ cmpxchg_weak(rtmp2, zr, rtmp1, Assembler::xword, memory_order_relaxed, rtmp3); __ br(Assembler::NE, slow_path); __ bind(slow_path_continuation); @@ -1388,9 +1385,8 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe __ bind(check_oop); // make sure klass is 'reasonable', which is not zero. - __ load_klass(tmp1, obj); // get klass - __ tst(tmp1, tmp1); - __ br(Assembler::EQ, error); // if klass is null it is broken + __ load_narrow_klass(tmp1, obj); // get narrow klass + __ cbz(tmp1, error); // if klass is null it is broken __ bind(check_zaddress); // Check if the oop is in the right area of memory diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index ad2e9243823..74e0395c81e 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -207,8 +207,7 @@ instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva Address ref_addr($mem$$Register); z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); - __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, memory_order_release); __ cset($res$$Register, Assembler::EQ); %} @@ -231,8 +230,7 @@ instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne Address ref_addr($mem$$Register); z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); - __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); %} @@ -255,7 +253,7 @@ instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); z_uncolor(masm, this, $res$$Register); %} @@ -278,7 +276,7 @@ instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); z_uncolor(masm, this, $res$$Register); %} diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp index 1e788590b64..30aa30aede9 100644 --- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp @@ -61,7 +61,9 @@ const bool CCallingConventionRequiresIntsAsLongs = false; // evidence that it's worth doing. #define DEOPTIMIZE_WHEN_PATCHING +#if !defined(_WINDOWS) #define SUPPORT_RESERVED_STACK_AREA +#endif #if defined(__APPLE__) || defined(_WIN64) #define R18_RESERVED diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index 59c7e44b0e5..1db73ff0306 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -48,7 +48,7 @@ define_pd_global(intx, OptoLoopAlignment, 16); // stack if compiled for unix and LP64. To pass stack overflow tests we need // 20 shadow pages. #define DEFAULT_STACK_SHADOW_PAGES (20 DEBUG_ONLY(+5)) -#define DEFAULT_STACK_RESERVED_PAGES (1) +#define DEFAULT_STACK_RESERVED_PAGES (NOT_WINDOWS(1) WINDOWS_ONLY(0)) #define MIN_STACK_YELLOW_PAGES DEFAULT_STACK_YELLOW_PAGES #define MIN_STACK_RED_PAGES DEFAULT_STACK_RED_PAGES diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 22c2383816c..0da237c133f 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1403,7 +1403,7 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md b(next); bind(update); - load_klass(obj, obj); + load_klass(obj, obj, rscratch1); ldr(rscratch1, mdo_addr); eor(obj, obj, rscratch1); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 1c052b67503..527e79459ec 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -25,6 +25,7 @@ #include "asm/assembler.hpp" #include "asm/assembler.inline.hpp" +#include "cds/archiveBuilder.hpp" #include "ci/ciEnv.hpp" #include "code/compiledIC.hpp" #include "compiler/compileTask.hpp" @@ -622,20 +623,18 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp, } } -static inline bool target_needs_far_branch(address addr) { +bool MacroAssembler::target_needs_far_branch(address addr) { if (AOTCodeCache::is_on_for_dump()) { return true; } - // codecache size <= 128M - if (!MacroAssembler::far_branches()) { + if (!far_branches()) { return false; } - // codecache size > 240M - if (MacroAssembler::codestub_branch_needs_far_jump()) { - return true; + if (CodeCache::is_non_nmethod(addr) && + CodeCache::max_distance_to_non_nmethod() <= branch_range) { + return false; } - // codecache size: 128M..240M - return !CodeCache::is_non_nmethod(addr); + return true; } void MacroAssembler::far_call(Address entry, Register tmp) { @@ -2231,8 +2230,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_ // offset is no longer needed after the address is computed. lea(rscratch2, Address(mdp, offset)); - cmpxchg(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, - /*acquire*/ false, /*release*/ false, /*weak*/ true, noreg); + cmpxchg_weak(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, memory_order_relaxed); // CAS success means the slot now has the receiver we want. CAS failure means // something had claimed the slot concurrently: it can be the same receiver we want, @@ -3494,9 +3492,33 @@ void MacroAssembler::reinit_heapbase() void MacroAssembler::cmpxchg(Register addr, Register expected, Register new_val, enum operand_size size, - bool acquire, bool release, + enum atomic_memory_order order, bool weak, Register result) { + bool acquire, release; + + switch (order) { + case memory_order_relaxed: + acquire = false; + release = false; + break; + case memory_order_acquire: + acquire = true; + release = false; + break; + case memory_order_release: + acquire = false; + release = true; + break; + case memory_order_acq_rel: + case memory_order_seq_cst: + acquire = true; + release = true; + break; + default: + ShouldNotReachHere(); + } + if (result == noreg) result = rscratch1; BLOCK_COMMENT("cmpxchg {"); if (UseLSE) { @@ -5074,7 +5096,7 @@ void MacroAssembler::load_method_holder(Register holder, Register method) { ldr(holder, Address(holder, ConstantPool::pool_holder_offset())); // InstanceKlass* } -// Loads the obj's Klass* into dst. +// Loads the obj's narrow Klass from a compact object header (+COH) into dst. // Preserves all registers (incl src, rscratch1 and rscratch2). // Input: // src - the oop we want to load the klass from. @@ -5085,13 +5107,18 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { lsr(dst, dst, markWord::klass_shift); } -void MacroAssembler::load_klass(Register dst, Register src) { +// Loads the obj's narrow Klass from any header (compact or not) into dst. +void MacroAssembler::load_narrow_klass(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); } else { ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); } - decode_klass_not_null(dst); +} + +void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { + load_narrow_klass(dst, src); + decode_klass_not_null(dst, dst, tmp); } void MacroAssembler::restore_cpu_control_state_after_jni(Register tmp1, Register tmp2) { @@ -5141,8 +5168,8 @@ void MacroAssembler::load_mirror(Register dst, Register method, Register tmp1, R resolve_oop_handle(dst, tmp1, tmp2); } -void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { - assert_different_registers(obj, klass, tmp); +void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp, Register tmp2) { + assert_different_registers(obj, klass, tmp, tmp2); if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp, obj); } else { @@ -5158,7 +5185,7 @@ void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { cmpw(klass, tmp); return; } - decode_klass_not_null(tmp); + decode_klass_not_null(tmp, tmp, tmp2); cmp(klass, tmp); } @@ -5173,11 +5200,11 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi cmpw(tmp1, tmp2); } -void MacroAssembler::store_klass(Register dst, Register src) { +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"); - encode_klass_not_null(src); + encode_klass_not_null(src, src, tmp); strw(src, Address(dst, oopDesc::klass_offset_in_bytes())); } @@ -5330,8 +5357,6 @@ MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode() { } MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode(address base, int shift, const size_t range) { - // KlassDecodeMode shouldn't be set already. - assert(_klass_decode_mode == KlassDecodeNone, "set once"); if (base == nullptr) { return KlassDecodeZero; @@ -5351,148 +5376,128 @@ MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode(address base, return KlassDecodeMovk; } - // No valid encoding. - return KlassDecodeNone; + return KlassDecodeFallback; } -// Check if one of the above decoding modes will work for given base, shift and range. -bool MacroAssembler::check_klass_decode_mode(address base, int shift, const size_t range) { - return klass_decode_mode(base, shift, range) != KlassDecodeNone; -} - -bool MacroAssembler::set_klass_decode_mode(address base, int shift, const size_t range) { +void MacroAssembler::initialize_klass_decode_mode(address base, int shift, const size_t range) { + // KlassDecodeMode shouldn't be set already. + assert(_klass_decode_mode == KlassDecodeNone, "set once"); _klass_decode_mode = klass_decode_mode(base, shift, range); - return _klass_decode_mode != KlassDecodeNone; + log_info(metaspace)("Klass Decode Mode: %d", (int)_klass_decode_mode); } -static Register pick_different_tmp(Register dst, Register src) { - auto tmps = RegSet::of(r0, r1, r2) - RegSet::of(src, dst); - return *tmps.begin(); +void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) { + emit_encode_klass_not_null(dst, src, tmp, CompressedKlassPointers::base(), + CompressedKlassPointers::shift(), klass_decode_mode()); } -void MacroAssembler::encode_klass_not_null_for_aot(Register dst, Register src) { - // we have to load the klass base from the AOT constants area but - // not the shift because it is not allowed to change - int shift = CompressedKlassPointers::shift(); - assert(shift >= 0 && shift <= CompressedKlassPointers::max_shift(), "unexpected compressed klass shift!"); - if (dst != src) { - // we can load the base into dst, subtract it formthe src and shift down - lea(dst, ExternalAddress(CompressedKlassPointers::base_addr())); - ldr(dst, dst); - sub(dst, src, dst); - lsr(dst, dst, shift); - } else { - // we need an extra register in order to load the coop base - Register tmp = pick_different_tmp(dst, src); - RegSet regs = RegSet::of(tmp); - push(regs, sp); +void MacroAssembler::emit_encode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode) { + + assert_different_registers(tmp, src); + assert(tmp != noreg, "valid tmp required"); + + if (AOTCodeCache::is_on_for_dump()) { + // We are generating code during AOT buildup that will run in *future* processes + // with likely different encoding settings. Therefore, we have to load the + // encoding base dynamically, we cannot just bake it in as immediate. + // Note that we only need to do this for base. The encoding shift would be the + // same between build time and runtime: the standard precomputed shift. + assert(shift == ArchiveBuilder::precomputed_narrow_klass_shift(), "unexpected compressed klass shift!"); lea(tmp, ExternalAddress(CompressedKlassPointers::base_addr())); ldr(tmp, tmp); sub(dst, src, tmp); lsr(dst, dst, shift); - pop(regs, sp); - } -} - -void MacroAssembler::encode_klass_not_null(Register dst, Register src) { - if (CompressedKlassPointers::base() != nullptr && AOTCodeCache::is_on_for_dump()) { - encode_klass_not_null_for_aot(dst, src); return; } - switch (klass_decode_mode()) { + switch (decode_mode) { case KlassDecodeZero: - if (CompressedKlassPointers::shift() != 0) { - lsr(dst, src, CompressedKlassPointers::shift()); - } else { - if (dst != src) mov(dst, src); - } + lsr(dst, src, shift); break; case KlassDecodeXor: - if (CompressedKlassPointers::shift() != 0) { - eor(dst, src, (uint64_t)CompressedKlassPointers::base()); - lsr(dst, dst, CompressedKlassPointers::shift()); - } else { - eor(dst, src, (uint64_t)CompressedKlassPointers::base()); - } + eor(dst, src, (uint64_t)base); + lsr(dst, dst, shift); break; case KlassDecodeMovk: - if (CompressedKlassPointers::shift() != 0) { - ubfx(dst, src, CompressedKlassPointers::shift(), 32); + if (shift != 0) { + ubfx(dst, src, shift, 32); } else { movw(dst, src); } break; + case KlassDecodeFallback: { + mov(tmp, base); + sub(dst, src, tmp); + lsr(dst, dst, shift); + break; + } + case KlassDecodeNone: ShouldNotReachHere(); break; } + +#ifdef ASSERT + if (tmp != dst) { + mov(tmp, 0xdead); + } +#endif // ASSERT + } -void MacroAssembler::encode_klass_not_null(Register r) { - encode_klass_not_null(r, r); +void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register tmp) { + emit_decode_klass_not_null(dst, src, tmp, + CompressedKlassPointers::base(), + CompressedKlassPointers::shift(), + klass_decode_mode()); } -void MacroAssembler::decode_klass_not_null_for_aot(Register dst, Register src) { - // we have to load the klass base from the AOT constants area but - // not the shift because it is not allowed to change - int shift = CompressedKlassPointers::shift(); - assert(shift >= 0 && shift <= CompressedKlassPointers::max_shift(), "unexpected compressed klass shift!"); - if (dst != src) { - // we can load the base into dst then add the offset with a suitable shift - lea(dst, ExternalAddress(CompressedKlassPointers::base_addr())); - ldr(dst, dst); - add(dst, dst, src, LSL, shift); - } else { - // we need an extra register in order to load the coop base - Register tmp = pick_different_tmp(dst, src); - RegSet regs = RegSet::of(tmp); - push(regs, sp); +void MacroAssembler::emit_decode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode) { + + assert_different_registers(tmp, src); + assert(tmp != noreg, "valid tmp required"); + + if (AOTCodeCache::is_on_for_dump()) { + // We are generating code during AOT buildup that will run in *future* processes + // with likely different encoding settings. Therefore, we have to load the + // encoding base dynamically, we cannot just bake it in as immediate. + // Note that we only need to do this for base. The encoding shift would be the + // same between build time and runtime: the standard precomputed shift. + assert(shift == ArchiveBuilder::precomputed_narrow_klass_shift(), "unexpected compressed klass shift!"); lea(tmp, ExternalAddress(CompressedKlassPointers::base_addr())); ldr(tmp, tmp); add(dst, tmp, src, LSL, shift); - pop(regs, sp); - } -} - -void MacroAssembler::decode_klass_not_null(Register dst, Register src) { - if (AOTCodeCache::is_on_for_dump()) { - decode_klass_not_null_for_aot(dst, src); return; } - switch (klass_decode_mode()) { - case KlassDecodeZero: - if (CompressedKlassPointers::shift() != 0) { - lsl(dst, src, CompressedKlassPointers::shift()); - } else { - if (dst != src) mov(dst, src); - } + switch (decode_mode) { + case KlassDecodeZero: // 0-1 instructions + lsl(dst, src, shift); break; - case KlassDecodeXor: - if (CompressedKlassPointers::shift() != 0) { - lsl(dst, src, CompressedKlassPointers::shift()); - eor(dst, dst, (uint64_t)CompressedKlassPointers::base()); - } else { - eor(dst, src, (uint64_t)CompressedKlassPointers::base()); - } + case KlassDecodeXor: // 1-2 instructions + lsl(dst, src, shift); + eor(dst, dst, (uint64_t)base); break; - case KlassDecodeMovk: { + case KlassDecodeMovk: { // 1-3 instructions const uint64_t shifted_base = - (uint64_t)CompressedKlassPointers::base() >> CompressedKlassPointers::shift(); + (uint64_t)base >> shift; if (dst != src) movw(dst, src); movk(dst, shifted_base >> 32, 32); + lsl(dst, dst, shift); + break; + } - if (CompressedKlassPointers::shift() != 0) { - lsl(dst, dst, CompressedKlassPointers::shift()); - } - + case KlassDecodeFallback: { // 3-4 instructions + mov(tmp, base); + add(dst, tmp, src, LSL, shift); break; } @@ -5500,10 +5505,14 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src) { ShouldNotReachHere(); break; } -} -void MacroAssembler::decode_klass_not_null(Register r) { - decode_klass_not_null(r, r); +#ifdef ASSERT + // Always clobber tmp + if (tmp != dst) { + mov(tmp, 0xdead); + } +#endif // ASSERT + } void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { @@ -7155,7 +7164,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R } if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(t1, obj); + load_klass(t1, obj, rscratch1); ldrb(t1, Address(t1, Klass::misc_flags_offset())); tst(t1, KlassFlags::_misc_is_value_based_class); br(Assembler::NE, slow); @@ -7180,8 +7189,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(mark, mark, markWord::unlocked_value); eor(t, mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, - /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, memory_order_acquire); br(Assembler::NE, slow); bind(push); @@ -7249,8 +7257,7 @@ void MacroAssembler::fast_unlock(Register obj, Register t1, Register t2, Registe // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(t, mark, markWord::unlocked_value); - cmpxchg(obj, mark, t, Assembler::xword, - /*acquire*/ false, /*release*/ true, /*weak*/ false, noreg); + cmpxchg(obj, mark, t, Assembler::xword, memory_order_release); br(Assembler::EQ, unlocked); bind(push_and_slow); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index a15b0630610..6dfdde51ac5 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -32,11 +32,13 @@ #include "metaprogramming/enableIf.hpp" #include "oops/compressedOops.hpp" #include "oops/compressedKlass.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/vm_version.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" class OopMap; +struct GtestFriendToMacroAssembler; // MacroAssembler extends Assembler by frequently used macros. // @@ -45,6 +47,7 @@ class OopMap; class MacroAssembler: public Assembler { friend class LIR_Assembler; + friend struct GtestFriendToMacroAssembler; public: using Assembler::mov; @@ -90,28 +93,31 @@ class MacroAssembler: public Assembler { void call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions = true); + private: + enum KlassDecodeMode { KlassDecodeNone, KlassDecodeZero, KlassDecodeXor, - KlassDecodeMovk + KlassDecodeMovk, + KlassDecodeFallback }; - // Calculate decoding mode based on given parameters, used for checking then ultimately setting. - static KlassDecodeMode klass_decode_mode(address base, int shift, const size_t range); - - private: static KlassDecodeMode _klass_decode_mode; // Returns above setting with asserts static KlassDecodeMode klass_decode_mode(); - public: - // Checks the decode mode and returns false if not compatible with preferred decoding mode. - static bool check_klass_decode_mode(address base, int shift, const size_t range); + // Calculate decoding mode based on given parameters, used for checking then ultimately setting. + static KlassDecodeMode klass_decode_mode(address base, int shift, const size_t range); - // Sets the decode mode and returns false if cannot be set. - static bool set_klass_decode_mode(address base, int shift, const size_t range); + void emit_encode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode); + void emit_decode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode); + public: + // Determines the decode mode best suited for the given encoding parameters. + static void initialize_klass_decode_mode(address base, int shift, const size_t range); public: MacroAssembler(CodeBuffer* code) : Assembler(code) {} @@ -307,19 +313,27 @@ class MacroAssembler: public Assembler { } inline void lslw(Register Rd, Register Rn, unsigned imm) { - ubfmw(Rd, Rn, ((32 - imm) & 31), (31 - imm)); + if (imm > 0 || Rd != Rn) { + ubfmw(Rd, Rn, ((32 - imm) & 31), (31 - imm)); + } } inline void lsl(Register Rd, Register Rn, unsigned imm) { - ubfm(Rd, Rn, ((64 - imm) & 63), (63 - imm)); + if (imm > 0 || Rd != Rn) { + ubfm(Rd, Rn, ((64 - imm) & 63), (63 - imm)); + } } inline void lsrw(Register Rd, Register Rn, unsigned imm) { - ubfmw(Rd, Rn, imm, 31); + if (imm > 0 || Rd != Rn) { + ubfmw(Rd, Rn, imm, 31); + } } inline void lsr(Register Rd, Register Rn, unsigned imm) { - ubfm(Rd, Rn, imm, 63); + if (imm > 0 || Rd != Rn) { + ubfm(Rd, Rn, imm, 63); + } } inline void rorw(Register Rd, Register Rn, unsigned imm) { @@ -923,9 +937,10 @@ public: // oop manipulations void load_narrow_klass_compact(Register dst, Register src); - void load_klass(Register dst, Register src); - void store_klass(Register dst, Register src); - void cmp_klass(Register obj, Register klass, Register tmp); + void load_narrow_klass(Register dst, Register src); + void load_klass(Register dst, Register src, Register tmp); + void store_klass(Register dst, Register src, Register tmp); + void cmp_klass(Register obj, Register klass, Register tmp, Register tmp2); void cmp_klasses_from_objects(Register obj1, Register obj2, Register tmp1, Register tmp2); void resolve_weak_handle(Register result, Register tmp1, Register tmp2); @@ -970,12 +985,8 @@ public: void set_narrow_oop(Register dst, jobject obj); - void decode_klass_not_null_for_aot(Register dst, Register src); - void encode_klass_not_null_for_aot(Register dst, Register src); - void encode_klass_not_null(Register r); - void decode_klass_not_null(Register r); - void encode_klass_not_null(Register dst, Register src); - void decode_klass_not_null(Register dst, Register src); + void encode_klass_not_null(Register dst, Register src, Register tmp); + void decode_klass_not_null(Register dst, Register src, Register tmp); void set_narrow_klass(Register dst, Klass* k); @@ -1239,12 +1250,25 @@ public: str(rscratch1, adr); } +private: // A generic CAS; success or failure is in the EQ flag. // Clobbers rscratch1 void cmpxchg(Register addr, Register expected, Register new_val, - enum operand_size size, - bool acquire, bool release, bool weak, - Register result); + enum operand_size size, enum atomic_memory_order order, + bool weak, Register result); + +public: + void cmpxchg(Register addr, Register expected, Register new_val, + enum operand_size size, enum atomic_memory_order order, + Register result = noreg) { + cmpxchg(addr, expected, new_val, size, order, /* weak */ false, result); + } + + void cmpxchg_weak(Register addr, Register expected, Register new_val, + enum operand_size size, enum atomic_memory_order order, + Register result = noreg) { + cmpxchg(addr, expected, new_val, size, order, /* weak */ true, result); + } #ifdef ASSERT // Template short-hand support to clean-up after a failed call to trampoline @@ -1339,15 +1363,16 @@ public: static bool far_branches() { return ReservedCodeCacheSize > branch_range; } - - // Check if branches to the non nmethod section require a far jump + // Check if the static call stub branch needs a far jump. static bool codestub_branch_needs_far_jump() { if (AOTCodeCache::is_on_for_dump()) { - // To calculate far_codestub_branch_size correctly. + // To calculate static_call_stub_size correctly. return true; } - return CodeCache::max_distance_to_non_nmethod() > branch_range; + return far_branches(); } + // Check if a branch to the given address needs a far jump. + static bool target_needs_far_branch(address addr); // Emit a direct call/jump if the entry address will always be in range, // otherwise a far call/jump. @@ -1359,18 +1384,10 @@ public: // In the case of a far call/jump, the entry address is put in the tmp register. // The tmp register is invalidated. // - // Far_jump returns the amount of the emitted code. void far_call(Address entry, Register tmp = rscratch1); + // Far_jump returns the amount of the emitted code. int far_jump(Address entry, Register tmp = rscratch1); - static int far_codestub_branch_size() { - if (codestub_branch_needs_far_jump()) { - return 3 * 4; // adrp, add, br - } else { - return 4; - } - } - // Emit the CompiledIC call idiom address ic_call(address entry, jint method_index = 0); static int ic_check_size(); @@ -1843,13 +1860,19 @@ public: #undef SVE_DESTRUCTIVE_TERNARY_INS - using Assembler::sve_eor3; - void sve_eor3(FloatRegister Zd, FloatRegister Zm, FloatRegister Zk) { - if (Zd != Zm && Zd != Zk) { - try_to_replace_prev_vector_copy_with_movprfx(Zd); - } - Assembler::sve_eor3(Zd, Zm, Zk); +#define SVE_DESTRUCTIVE_TERNARY_UNPRED_INS(NAME) \ + using Assembler::NAME; \ + void NAME(FloatRegister Zd, FloatRegister Zm, FloatRegister Zk) { \ + if (Zd != Zm && Zd != Zk) { \ + try_to_replace_prev_vector_copy_with_movprfx(Zd); \ + } \ + Assembler::NAME(Zd, Zm, Zk); \ } + + SVE_DESTRUCTIVE_TERNARY_UNPRED_INS(sve_bsl); + SVE_DESTRUCTIVE_TERNARY_UNPRED_INS(sve_eor3); + +#undef SVE_DESTRUCTIVE_TERNARY_UNPRED_INS }; #ifdef ASSERT diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index cdf67e3423f..7dc74f44cdc 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -76,7 +76,7 @@ void MethodHandles::verify_klass(MacroAssembler* _masm, __ verify_oop(obj); __ cbz(obj, L_bad); __ push(RegSet::of(temp, temp2), sp); - __ load_klass(temp, obj); + __ load_klass(temp, obj, temp2); __ cmpptr(temp, ExternalAddress((address) klass_addr)); __ br(Assembler::EQ, L_ok); intptr_t super_check_offset = klass->super_check_offset(); @@ -368,7 +368,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, __ null_check(receiver_reg); } else { // load receiver klass itself - __ load_klass(temp1_recv_klass, receiver_reg); + __ load_klass(temp1_recv_klass, receiver_reg, temp2); __ verify_klass_ptr(temp1_recv_klass); } BLOCK_COMMENT("check_receiver {"); @@ -376,7 +376,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, // Check the receiver against the MemberName.clazz if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) { // Did not load it above... - __ load_klass(temp1_recv_klass, receiver_reg); + __ load_klass(temp1_recv_klass, receiver_reg, temp2); __ verify_klass_ptr(temp1_recv_klass); } if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) { diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index 2ddea2fdcb5..e17993b4c8a 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -123,7 +123,7 @@ void NativeCall::insert(address code_pos, address entry) { Unimplemented(); } void NativeMovConstReg::verify() { if (! (nativeInstruction_at(instruction_address())->is_movz() || is_adrp_at(instruction_address()) || - is_ldr_literal_at(instruction_address())) ) { + is_load_literal_at(instruction_address())) ) { fatal("should be MOVZ or ADRP or LDR (literal)"); } } @@ -270,17 +270,17 @@ bool NativeInstruction::is_safepoint_poll() { // a safepoint_poll is implemented in two steps as either // // adrp(reg, polling_page); - // ldr(zr, [reg, #offset]); + // ldrw(zr, [reg, #offset]); // // or // // mov(reg, polling_page); - // ldr(zr, [reg, #offset]); + // ldrw(zr, [reg, #offset]); // // or // // ldr(reg, [rthread, #offset]); - // ldr(zr, [reg, #offset]); + // ldrw(zr, [reg, #offset]); // // however, we cannot rely on the polling page address load always // directly preceding the read from the page. C1 does that but C2 @@ -301,11 +301,21 @@ bool NativeInstruction::is_adrp_at(address instr) { return (Instruction_aarch64::extract(insn, 31, 24) & 0b10011111) == 0b10010000; } -bool NativeInstruction::is_ldr_literal_at(address instr) { +bool NativeInstruction::is_load_literal_at(address instr) { unsigned insn = *(unsigned*)instr; return (Instruction_aarch64::extract(insn, 29, 24) & 0b011011) == 0b00011000; } +bool NativeInstruction::is_ldr_gpr_literal_at(address instr) { + unsigned insn = *(unsigned*)instr; + return Instruction_aarch64::extract(insn, 31, 24) == 0b01011000; +} + +bool NativeInstruction::is_ldrw_gpr_literal_at(address instr) { + unsigned insn = *(unsigned*)instr; + return Instruction_aarch64::extract(insn, 31, 24) == 0b00011000; +} + bool NativeInstruction::is_ldrw_to_zr(address instr) { unsigned insn = *(unsigned*)instr; return (Instruction_aarch64::extract(insn, 31, 22) == 0b1011100101 && diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index 57bb9a91533..c406b471f20 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -107,10 +107,22 @@ public: static bool is_adrp_at(address instr); - static bool is_ldr_literal_at(address instr); + static bool is_load_literal_at(address instr); - bool is_ldr_literal() { - return is_ldr_literal_at(addr_at(0)); + bool is_load_literal() { + return is_load_literal_at(addr_at(0)); + } + + static bool is_ldr_gpr_literal_at(address instr); + + bool is_ldr_gpr_literal() { + return is_ldr_gpr_literal_at(addr_at(0)); + } + + static bool is_ldrw_gpr_literal_at(address instr); + + bool is_ldrw_gpr_literal() { + return is_ldrw_gpr_literal_at(addr_at(0)); } static bool is_ldrw_to_zr(address instr); @@ -125,7 +137,7 @@ public: } static bool maybe_cpool_ref(address instr) { - return is_adrp_at(instr) || is_ldr_literal_at(instr); + return is_adrp_at(instr) || is_load_literal_at(instr); } bool is_Membar() { @@ -267,7 +279,7 @@ public: return addr_at(instruction_size); else if (is_adrp_at(instruction_address())) return addr_at(2*4); - else if (is_ldr_literal_at(instruction_address())) + else if (is_load_literal_at(instruction_address())) return(addr_at(4)); assert(false, "Unknown instruction in NativeMovConstReg"); return nullptr; diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index d1e0632c80b..8d8856d3cf9 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -28,6 +28,7 @@ #include "asm/register.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" class VMRegImpl; @@ -513,28 +514,39 @@ template bool vs_write_before_read(const VSeq& vout, const VSeq& vi template VSeq vs_front(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base(), v.delta()); } template VSeq vs_back(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base() + N / 2 * v.delta(), v.delta()); } template VSeq vs_even(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base(), v.delta() * 2); } template VSeq vs_odd(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base() + v.delta(), v.delta() * 2); } +template +FloatRegister vs_head(const VSeq& v) { + static_assert(N > 1, "sequence length must be greater than 1"); + return v.base(); +} + +template +VSeq vs_tail(const VSeq& v) { + return VSeq(v.base() + v.delta(), v.delta()); +} + // convenience method to construct a vector register sequence that // indexes its elements in reverse order to the original diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index f1b9fb213a2..5d6bcb45613 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -41,7 +41,7 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { case relocInfo::oop_type: { oop_Relocation *reloc = (oop_Relocation *)this; - if (NativeInstruction::is_ldr_literal_at(addr())) { + if (NativeInstruction::is_load_literal_at(addr())) { address constptr = (address)code()->oop_addr_at(reloc->oop_index()); bytes = MacroAssembler::pd_patch_instruction_size(addr(), constptr); assert(*(address*)constptr == x, "error in oop relocation"); diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp index d1f59e479db..d1e0621f6a9 100644 --- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp @@ -57,7 +57,7 @@ do_arch_entry, \ do_arch_entry_init, \ do_arch_entry_array) \ - do_arch_blob(compiler, 70000) \ + do_arch_blob(compiler, 75000) \ do_stub(compiler, vector_iota_indices) \ do_arch_entry_array(aarch64, compiler, vector_iota_indices, \ vector_iota_indices, vector_iota_indices, \ diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 8e9af2b7b8a..03eb5084eb4 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -152,6 +152,12 @@ static const uint64_t _double_keccak_round_consts[24] = { 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L }; +//Omit 3rd limb of modulus since it is 0 +static const int64_t _modulus_P256[5] = { + 0x000fffffffffffffL, 0x00000fffffffffffL, + 0x0000001000000000L, 0x0000ffffffff0000L +}; + 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', @@ -2252,7 +2258,7 @@ class StubGenerator: public StubCodeGenerator { // checked. assert_different_registers(from, to, count, ckoff, ckval, start_to, - copied_oop, r19_klass, count_save); + copied_oop, r19_klass, count_save, rscratch1); __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); @@ -2336,7 +2342,7 @@ class StubGenerator: public StubCodeGenerator { gct1); __ cbz(copied_oop, L_store_element); - __ load_klass(r19_klass, copied_oop);// query the object klass + __ load_klass(r19_klass, copied_oop, rscratch1);// query the object klass BLOCK_COMMENT("type_check:"); generate_type_check(/*sub_klass*/r19_klass, @@ -2562,7 +2568,7 @@ class StubGenerator: public StubCodeGenerator { __ movw(scratch_length, length); // length (elements count, 32-bits value) __ tbnz(scratch_length, 31, L_failed); // i.e. sign bit set - __ load_klass(scratch_src_klass, src); + __ load_narrow_klass(scratch_src_klass, src); #ifdef ASSERT // assert(src->klass() != nullptr); { @@ -2572,11 +2578,12 @@ class StubGenerator: public StubCodeGenerator { __ bind(L1); __ stop("broken null klass"); __ bind(L2); - __ load_klass(rscratch1, dst); + __ load_narrow_klass(rscratch1, dst); __ cbz(rscratch1, L1); // this would be broken also BLOCK_COMMENT("} assert klasses not null done"); } #endif + __ decode_klass_not_null(scratch_src_klass, scratch_src_klass, rscratch1); // Load layout helper (32-bits) // @@ -2596,7 +2603,7 @@ class StubGenerator: public StubCodeGenerator { __ cbzw(rscratch2, L_objArray); // if (src->klass() != dst->klass()) return -1; - __ load_klass(rscratch2, dst); + __ load_klass(rscratch2, dst, rscratch1); __ eor(rscratch2, rscratch2, scratch_src_klass); __ cbnz(rscratch2, L_failed); @@ -2692,7 +2699,7 @@ class StubGenerator: public StubCodeGenerator { Label L_plain_copy, L_checkcast_copy; // test array classes for subtyping - __ load_klass(r15, dst); + __ load_klass(r15, dst, rscratch1); __ cmp(scratch_src_klass, r15); // usual case is exact equality __ br(Assembler::NE, L_checkcast_copy); @@ -2721,7 +2728,7 @@ class StubGenerator: public StubCodeGenerator { arraycopy_range_checks(src, src_pos, dst, dst_pos, scratch_length, r15, L_failed); - __ load_klass(dst_klass, dst); // reload + __ load_klass(dst_klass, dst, rscratch1); // reload // Marshal the base address arguments now, freeing registers. __ lea(from, Address(src, src_pos, Address::lsl(LogBytesPerHeapOop))); @@ -4977,7 +4984,7 @@ class StubGenerator: public StubCodeGenerator { return start; } // Implements the double_keccak() method of the - // sun.secyrity.provider.SHA3Parallel class + // sun.security.provider.SHA3Parallel class __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); start = __ pc(); @@ -5045,7 +5052,8 @@ class StubGenerator: public StubCodeGenerator { __ ldpd(v8, v9, __ post(sp, 64)); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -5310,6 +5318,32 @@ class StubGenerator: public StubCodeGenerator { } } + template + void vs_shl(const VSeq& v, Assembler::SIMD_Arrangement T, + const VSeq& v1, int shift) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + + for (int i = 0; i < N; i++) { + __ shl(v[i], T, v1[i], shift); + } + } + + template + void vs_ushr(const VSeq& v, Assembler::SIMD_Arrangement T, + const VSeq& v1, int shift) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + + for (int i = 0; i < N; i++) { + __ ushr(v[i], T, v1[i], shift); + } + } + template void vs_sshr(const VSeq& v, Assembler::SIMD_Arrangement T, const VSeq& v1, int shift) { @@ -5334,6 +5368,29 @@ class StubGenerator: public StubCodeGenerator { } } + template + void vs_andr(const VSeq& v, const VSeq& v1, const FloatRegister v2) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + for (int i = 0; i < N; i++) { + __ andr(v[i], __ T16B, v1[i], v2); + } + } + + template + void vs_eor(const VSeq& v, const VSeq& v1, const VSeq& v2) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + assert(!vs_write_before_read(v, v2), "output overwrites input"); + for (int i = 0; i < N; i++) { + __ eor(v[i], __ T16B, v1[i], v2[i]); + } + } + template void vs_orr(const VSeq& v, const VSeq& v1, const VSeq& v2) { // output must not be constant @@ -5386,8 +5443,9 @@ class StubGenerator: public StubCodeGenerator { // address supplied in base. template void vs_ldpq(const VSeq& v, Register base) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { - __ ldpq(v[i], v[i+1], Address(base, 32 * i)); + __ ldpq(v[i], v[i+1], Address(base, 16 * i)); } } @@ -5396,7 +5454,7 @@ class StubGenerator: public StubCodeGenerator { // in base using post-increment addressing template void vs_ldpq_post(const VSeq& v, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ldpq(v[i], v[i+1], __ post(base, 32)); } @@ -5407,7 +5465,7 @@ class StubGenerator: public StubCodeGenerator { // supplied in base using post-increment addressing template void vs_stpq_post(const VSeq& v, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ stpq(v[i], v[i+1], __ post(base, 32)); } @@ -5418,7 +5476,7 @@ class StubGenerator: public StubCodeGenerator { // using post-increment addressing. template void vs_ld2_post(const VSeq& v, Assembler::SIMD_Arrangement T, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ld2(v[i], v[i+1], T, __ post(base, 32)); } @@ -5429,12 +5487,24 @@ class StubGenerator: public StubCodeGenerator { // post-increment addressing. template void vs_st2_post(const VSeq& v, Assembler::SIMD_Arrangement T, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ st2(v[i], v[i+1], T, __ post(base, 32)); } } + // store two vector register sequences of length N + // interleaved into N pairs of quadword memory locations + // starting at the address supplied in dest using + // post-increment addressing. + template + void vs_st1_interleaved(VSeq A, VSeq B, Register dest) { + for (int i = 0; i < N; i++) { + __ st1(A[i], __ T2D, __ post(dest, 16)); + __ st1(B[i], __ T2D, __ post(dest, 16)); + } + } + // load N quadword values from memory de-interleaved into N vector // registers 3 elements at a time via the address supplied in base. template @@ -5458,10 +5528,11 @@ class StubGenerator: public StubCodeGenerator { // load N/2 pairs of quadword values from memory into N vector // registers via the address supplied in base with each pair indexed - // using the the start offset plus the corresponding entry in the + // using the start offset plus the corresponding entry in the // offsets array template void vs_ldpq_indexed(const VSeq& v, Register base, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ ldpq(v[2*i], v[2*i+1], Address(base, start + offsets[i])); } @@ -5469,7 +5540,7 @@ class StubGenerator: public StubCodeGenerator { // store N vector registers into N/2 pairs of quadword memory // locations via the address supplied in base with each pair indexed - // using the the start offset plus the corresponding entry in the + // using the start offset plus the corresponding entry in the // offsets array template void vs_stpq_indexed(const VSeq& v, Register base, int start, int offsets[N/2]) { @@ -5480,7 +5551,7 @@ class StubGenerator: public StubCodeGenerator { // load N single quadword values from memory into N vector registers // via the address supplied in base with each value indexed using - // the the start offset plus the corresponding entry in the offsets + // the start offset plus the corresponding entry in the offsets // array template void vs_ldr_indexed(const VSeq& v, Assembler::SIMD_RegVariant T, Register base, @@ -5492,7 +5563,7 @@ class StubGenerator: public StubCodeGenerator { // store N vector registers into N single quadword memory locations // via the address supplied in base with each value indexed using - // the the start offset plus the corresponding entry in the offsets + // the start offset plus the corresponding entry in the offsets // array template void vs_str_indexed(const VSeq& v, Assembler::SIMD_RegVariant T, Register base, @@ -5504,11 +5575,12 @@ class StubGenerator: public StubCodeGenerator { // load N/2 pairs of quadword values from memory de-interleaved into // N vector registers 2 at a time via the address supplied in base - // with each pair indexed using the the start offset plus the + // with each pair indexed using the start offset plus the // corresponding entry in the offsets array template void vs_ld2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, Register tmp, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ add(tmp, base, start + offsets[i]); __ ld2(v[2*i], v[2*i+1], T, tmp); @@ -5517,11 +5589,12 @@ class StubGenerator: public StubCodeGenerator { // store N vector registers 2 at a time interleaved into N/2 pairs // of quadword memory locations via the address supplied in base - // with each pair indexed using the the start offset plus the + // with each pair indexed using the start offset plus the // corresponding entry in the offsets array template void vs_st2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, Register tmp, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ add(tmp, base, start + offsets[i]); __ st2(v[2*i], v[2*i+1], T, tmp); @@ -5776,7 +5849,7 @@ class StubGenerator: public StubCodeGenerator { // registers. // 3. In the seilerNTT() method we use R = 2^20 for the Montgomery // multiplications (this is because that way there should not be any - // overflow during the inverse NTT computation), here we usr R = 2^16 so + // overflow during the inverse NTT computation), here we use R = 2^16 so // that we can use the 16-bit arithmetic in the vector unit. // // On each level, we fill up the vector registers in such a way that the @@ -5898,7 +5971,7 @@ class StubGenerator: public StubCodeGenerator { // level 4 // At level 4 coefficients occur in 8 discrete blocks of size 16 - // so they are loaded using employing an ldr at 8 distinct offsets. + // so they are loaded by employing an ldr at 8 distinct offsets. vs_ldpq(vq, kyberConsts); int offsets3[8] = { 0, 32, 64, 96, 128, 160, 192, 224 }; @@ -5954,7 +6027,6 @@ class StubGenerator: public StubCodeGenerator { kyber_montmul32_sub_add(vs_even(vs1), vs_odd(vs1), vs_front(vs2), vtmp, vq); vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, 0, offsets4); vs_ld2_indexed(vs1, __ T4S, coeffs, tmpAddr, 128, offsets4); - // __ ldpq(v18, v19, __ post(zetas, 32)); load32shorts(vs_front(vs2), zetas); kyber_montmul32_sub_add(vs_even(vs1), vs_odd(vs1), vs_front(vs2), vtmp, vq); vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, 128, offsets4); @@ -5970,7 +6042,7 @@ class StubGenerator: public StubCodeGenerator { vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, 384, offsets4); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6067,7 +6139,7 @@ class StubGenerator: public StubCodeGenerator { // level 2 // At level 2 coefficients occur in 8 discrete blocks of size 16 - // so they are loaded using employing an ldr at 8 distinct offsets. + // so they are loaded by employing an ldr at 8 distinct offsets. int offsets3[8] = { 0, 32, 64, 96, 128, 160, 192, 224 }; vs_ldr_indexed(vs1, __ Q, coeffs, 0, offsets3); @@ -6262,7 +6334,7 @@ class StubGenerator: public StubCodeGenerator { store64shorts(vs2, tmpAddr); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6407,7 +6479,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::NE, kyberNttMult_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6499,7 +6571,7 @@ class StubGenerator: public StubCodeGenerator { } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6606,7 +6678,7 @@ class StubGenerator: public StubCodeGenerator { } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6692,8 +6764,8 @@ class StubGenerator: public StubCodeGenerator { // twice, one copy manipulated to provide the lower 4 bits // belonging to the first short in a pair and another copy // manipulated to provide the higher 4 bits belonging to the - // second short in a pair. This is why the the vector sequences va - // and vb used to hold the expanded 8H elements are of length 8. + // second short in a pair. This is why the vector sequences va + // and vb are used to hold the expanded 8H elements are of length 8. // Expand vin[0] into va[0:1], and vin[1] into va[2:3] and va[4:5] // n.b. target elements 2 and 3 duplicate elements 4 and 5 @@ -6763,7 +6835,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::GT, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // bind label and generate constant data used by this stub @@ -6869,7 +6941,7 @@ class StubGenerator: public StubCodeGenerator { } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6943,7 +7015,7 @@ class StubGenerator: public StubCodeGenerator { vs_addv(va0, __ T4S, va0, vc); } - // Perform combined add/sub then montul on 4x4S vectors. + // Perform combined add/sub then montmul on 4x4S vectors. void dilithium_sub_add_montmul16( const VSeq<4>& va0, const VSeq<4>& va1, const VSeq<4>& vb, const VSeq<4>& vtmp1, const VSeq<4>& vtmp2, const VSeq<2>& vq) { @@ -7079,7 +7151,7 @@ class StubGenerator: public StubCodeGenerator { // coefficients we load 4 adjacent values at 8 different offsets // using an indexed ldr with register variant Q and multiply them // in sequence order by the next set of inputs. Likewise we store - // the resuls using an indexed str with register variant Q. + // the results using an indexed str with register variant Q. for (int i = 0; i < 1024; i += 256) { // reload constants q, qinv each iteration as they get clobbered later vs_ldpq(vq, dilithiumConsts); // qInv, q @@ -7129,11 +7201,11 @@ class StubGenerator: public StubCodeGenerator { // level 7 // At level 7 the coefficients we need to combine with the zetas - // occur singly with montmul inputs alterating with add/sub + // occur singly with montmul inputs alternating with add/sub // inputs. Once again we can use 4-way parallelism to combine 16 // zetas at a time. However, we have to load 8 adjacent values at // 4 different offsets using an ld2 load with arrangement 4S. That - // interleaves the the odd words of each pair into one + // interleaves the odd words of each pair into one // coefficients vector register and the even words of the pair // into the next register. We then need to montmul the 4 even // elements of the coefficients register sequence by the zetas in @@ -7155,7 +7227,7 @@ class StubGenerator: public StubCodeGenerator { vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, i, offsets); } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7334,7 +7406,7 @@ class StubGenerator: public StubCodeGenerator { // c0 load 32 (8x4S) coefficients via first offsets vs_ldr_indexed(vs1, __ Q, coeffs, i, offsets1); // c1 load 32 (8x4S) coefficients via second offsets - vs_ldr_indexed(vs2, __ Q,coeffs, i, offsets2); + vs_ldr_indexed(vs2, __ Q, coeffs, i, offsets2); // a0 = c0 + c1 n.b. clobbers vq which overlaps vs3 vs_addv(vs3, __ T4S, vs1, vs2); // c = c0 - c1 @@ -7355,7 +7427,7 @@ class StubGenerator: public StubCodeGenerator { dilithiumInverseNttLevel3_7(dilithiumConsts, coeffs, zetas); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7367,8 +7439,8 @@ class StubGenerator: public StubCodeGenerator { // Dilithium multiply polynomials in the NTT domain. // Straightforward implementation of the method // static int implDilithiumNttMult( - // int[] result, int[] ntta, int[] nttb {} of - // the sun.security.provider.ML_DSA class. + // int[] product, int[] coeffs1, int[] coeffs2) {} + // of the sun.security.provider.ML_DSA class. // // result (int[256]) = c_rarg0 // poly1 (int[256]) = c_rarg1 @@ -7429,7 +7501,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::GE, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7438,10 +7510,10 @@ class StubGenerator: public StubCodeGenerator { return start; } - // Dilithium Motgomery multiply an array by a constant. + // Dilithium Montgomery multiply an array by a constant. // A straightforward implementation of the method // static int implDilithiumMontMulByConstant(int[] coeffs, int constant) {} - // of the sun.security.provider.MLDSA class + // of the sun.security.provider.ML_DSA class // // coeffs (int[256]) = c_rarg0 // constant (int) = c_rarg1 @@ -7498,7 +7570,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::GE, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7509,7 +7581,8 @@ class StubGenerator: public StubCodeGenerator { // Dilithium decompose poly. // Implements the method - // static int implDilithiumDecomposePoly(int[] coeffs, int constant) {} + // static int implDilithiumDecomposePoly(int[] input, int[] lowPart, int[] highPart, + // int twoGamma2, int multiplier) { // of the sun.security.provider.ML_DSA class // // input (int[256]) = c_rarg0 @@ -7613,7 +7686,7 @@ class StubGenerator: public StubCodeGenerator { vs_andr(vtmp, vs4, twog2); vs_subv(vs3, __ T4S, vs3, vtmp); - // quotient += (mask & 1); + // quotient += (mask & 1); vs_andr(vtmp, vs4, one); vs_addv(vs2, __ T4S, vs2, vtmp); @@ -7647,7 +7720,7 @@ class StubGenerator: public StubCodeGenerator { // r1 = r1 & quotient; vs_andr(vs1, vs2, vs1); - // store results inteleaved + // store results interleaved // lowPart[m] = r0; // highPart[m] = r1; __ st4(vs3[0], vs3[1], vs3[2], vs3[3], __ T4S, __ post(lowPart, 64)); @@ -7664,6 +7737,587 @@ class StubGenerator: public StubCodeGenerator { __ ldpd(v8, v9, __ post(sp, 64)); __ leave(); // required for proper stackwalking of RuntimeStub frame + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + + static constexpr int montMulP256Shift1 = 12; // 64 - bits per limb + static constexpr int montMulP256Shift2 = 52; // bits per limb + // stack space needed for carry computation + static constexpr int cDataSize = 6 * BytesPerLong; + // stack space needed for data computed by the neon side + static constexpr int mulDataSize = 16 * BytesPerLong; + + + // Subroutine used by the 52 x 52 bit multiplication algorithm in + // generate_intpoly_montgomeryMult_P256(). + // This function computes partial results of eight 52 x 52 bit multiplications, + // where the multiplicands are stored as 64-bit values, specifically + // (b_0, b_1, b_2, b_3) * (a_3, a_4). (The 4 calls to this function + // together provide the results of these limb-multiplications.) + // Calls to this function accept either the low 32 bits or high 20 bits + // of each b_i packed into bs in ascending order. a_3 and a_4 are packed + // into successive 64 bit elements of as. lane selects the low 32 or high + // 20 bits of each a_j value. So four calls with the appropriate parameters + // will produce the 64-bit low32 * low32, low32 * high20, high20 * low32, + // high20 * high20 values in the output register sequences vs. The + // 64-bit partial products are returned in vs in ascending order: + // vs[0] = (b_0*a_3, b_1*a_3) . . . vs[3] = (b_2*a_4, b_3*a_4) + + void neon_partial_mult_64(const VSeq<4>& vs, FloatRegister bs, FloatRegister as, int lane_lo) { + __ umullv(vs[0], __ T2D, bs, __ T2S, as, __ S, lane_lo); + __ umull2v(vs[1], __ T2D, bs, __ T4S, as, __ S, lane_lo); + __ umullv(vs[2], __ T2D, bs, __ T2S, as, __ S, lane_lo + 2); + __ umull2v(vs[3], __ T2D, bs, __ T4S, as, __ S, lane_lo + 2); + } + + // Subroutine used by the generate_intpoly_montgomeryMult_P256() function + // to compute the result of a 52 x 52 bit multiplications where the + // multiplicands, a and b are available as 64-bit values. + // The result is going to two 64-bit registers lo (least significant 52 bits) + // and hi (most significant 52 bits). + void gpr_partial_mult_52(Register a, Register b, Register hi, Register lo, + Register mask) { + // compute 104-bit (40 + 64) full product + __ umulh(hi, a, b); + __ mul(lo, a, b); + // combine 40 + 12 bits into hi result + // on certain implementations of aarch64 (e.g. apple M1) replacing extr() + // with the following equivalent instruction sequence the performance + // improves slightly (despite it is two instructions longer and needs + // an additional register) + // __ lsl(hi, hi, montMulP256Shift1); + // __ lsr(tmp, lo, montMulP256Shift2); + // __ orr(hi, hi, tmp); + __ extr(hi, hi, lo, montMulP256Shift2); + // mask off 52 bits of lo result + __ andr(lo, lo, mask); + } + + // This assembly follows the Java code in MontgomeryIntegerPolynomial256.mult() + // quite closely. The main difference is that the computations done with the + // last two limbs of `a` are done using Neon registers. This allows us to take + // advantage of both the Neon registers and GPRs simultaneously. + // It is also worth noting that since Neon does not support 64 bit + // multiplication, we split each 64 bit value into lower and upper halves + // and use the "schoolbook" multiplication algorithm. + address generate_intpoly_montgomeryMult_P256() { + assert(UseIntPolyIntrinsics, "what are we doing here?"); + 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); + start = __ pc(); + __ enter(); + + // Registers that are used throughout entire routine + const Register a = c_rarg0; + const Register b = c_rarg1; + const Register result = c_rarg2; + + RegSet regs = RegSet::range(r0, r28) - rscratch1 - rscratch2 + - r16 - r17 - r18_tls - a - b - result; + + auto common_regs = regs.begin(); + Register limb_mask = *common_regs++, + c_ptr = *common_regs++, + mod_0 = *common_regs++, + mod_1 = *common_regs++, + mod_3 = *common_regs++, + mod_4 = *common_regs++, + b_0 = *common_regs++, + b_1 = *common_regs++, + b_2 = *common_regs++, + b_3 = *common_regs++, + b_4 = *common_regs++; + + FloatRegSet floatRegs = FloatRegSet::range(v0, v31) + - FloatRegSet::range(v8, v15) // Caller saved vectors + - FloatRegSet::range(v16, v31); // Manually-allocated vectors + + auto common_vectors = floatRegs.begin(); + FloatRegister limb_mask_vec = *common_vectors++, + b_lows = *common_vectors++, + b_highs = *common_vectors++, + a_vals = *common_vectors++; + + // Push callee saved registers on to the stack + RegSet callee_saved = RegSet::range(r19, r28); + __ push(callee_saved, sp); + + // Allocate space on the stack for carry values + __ sub(sp, sp, cDataSize); + __ mov(c_ptr, sp); + + // Calculate (52-bit) limb masks for both gpr and vector registers + __ mov(limb_mask, -UCONST64(1) >> montMulP256Shift1); + __ dup(limb_mask_vec, __ T2D, limb_mask); + + //Load input arrays and modulus + Register a_ptr = *common_regs++, mod_ptr = *common_regs++; + // skip 3 limbs so a_ptr addresses trailing pair {a3, a4} + __ add(a_ptr, a, 3 * BytesPerLong); + __ lea(mod_ptr, ExternalAddress((address)_modulus_P256)); + __ ldr(b_0, Address(b)); + __ ldr(b_1, Address(b, BytesPerLong)); + __ ldr(b_2, Address(b, 2 * BytesPerLong)); + __ ldr(b_3, Address(b, 3 * BytesPerLong)); + __ ldr(b_4, Address(b, 4 * BytesPerLong)); + __ ldr(mod_0, __ post(mod_ptr, BytesPerLong)); + __ ldr(mod_1, __ post(mod_ptr, BytesPerLong)); + __ ldr(mod_3, __ post(mod_ptr, BytesPerLong)); + __ ldr(mod_4, mod_ptr); + __ ld1(a_vals, __ T2D, a_ptr); + // use an interleaved load to group low 32 bits and high 20 bits + // of 4 successive b values into two vector registers + // n.b. these are the same inputs as the ones in b_0 ... b4 + __ ld2(b_lows, b_highs, __ T4S, b); + common_regs = common_regs.remaining() + + a_ptr + mod_ptr; + a_ptr = mod_ptr = noreg; + + //Regs used throughout the main "loop", which is partially unrolled here + Register high = *common_regs++, + low = *common_regs++, + mul_ptr = *common_regs++, + mod_high = *common_regs++, + mod_low = *common_regs++, + a_i = *common_regs++, + c_i = *common_regs++, + tmp = *common_regs++, + n = *common_regs++; + + // vector sequences used to compute and combine partial products of + // b_i * a_j for i = {0,1,2,3} j = {3,4} + VSeq<4> A(16); + VSeq<4> B(20); + VSeq<4> C(24); + VSeq<4> D(28); + + + // neon and gpr computations are interleaved to maximize parallelism + + // allocate stack space for the neon results + __ sub(sp, sp, mulDataSize); + __ mov(mul_ptr, sp); + + // cross-multiply low * low for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(A, b_lows, a_vals, 0); + + // Limb 0 + __ ldr(a_i, __ post(a, BytesPerLong)); + gpr_partial_mult_52(a_i, b_0, high, low, limb_mask); + __ mov(n, low); + // __ andr(n, low, limb_mask); + + // cross-multiply high * low for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(B, b_highs, a_vals, 0); + + // Limb 0 modulus computation + // n.b. modulus computation requires multiplying successive + // limbs of the product by corresponding limbs of the p256 + // prime adding the result to the limb and folding this + // partial result into a running 256-bit sum in c_i. Limbs + // of c_i are stored via c_ptr once carries are included. + // n.b. the mul + add is omitted for limb 2 since the + // corresponding prime bits are zero. + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ lsr(c_i, low, montMulP256Shift2); + __ add(c_i, c_i, high); + + // cross-multiply low * high for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(C, b_lows, a_vals, 1); + + // Limb 1 + gpr_partial_mult_52(a_i, b_1, high, low, limb_mask); + + // cross-multiply high * high for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(D, b_highs, a_vals, 1); + + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, c_ptr); + __ mov(c_i, high); + + // combine neon 32-bit partial products, regrouping to produce + // 8*52-bit low products in A and 8*52-bit high products in D + + // add low*high/high*low intermediate products before regrouping + vs_addv(B, __ T2D, B, C); // Store (B+C) in B + + // Limb 2 + gpr_partial_mult_52(a_i, b_2, high, low, limb_mask); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 8)); + __ mov(c_i, high); + + // shift high*high (40-bit) product up into 52-bits of output + vs_shl(D, __ T2D, D, montMulP256Shift1); + + // Limb 3 + gpr_partial_mult_52(a_i, b_3, high, low, limb_mask); + + // shift high 32 (or 33) bits of intermediate products for addition to D + vs_ushr(C, __ T2D, B, 32 - montMulP256Shift1); // Use C for ((B+C) >>> 20) + + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ mov(c_i, high); + + // shift low 32 bits of intermediate product up for masking and addition to A + vs_shl(B, __ T2D, B, 32); + + // Limb 4 + gpr_partial_mult_52(a_i, b_4, high, low, limb_mask); + + // add high bits of intermediate product into D + vs_addv(D, __ T2D, D, C); + + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ str(high, Address(c_ptr, 4 * BytesPerLong)); + + // top 12 bits of 32*32 bit product in A need adding into high 52-bit output + vs_ushr(C, __ T2D, A, 52); // C now holds (A >>> 52) + // Only 20 of the 32 bits now in the top of B should be added into A + vs_andr(B, B, limb_mask_vec); + // reduce original 64-bit product to 52-bits + vs_andr(A, A, limb_mask_vec); + // add intermediate products to high 52-bit result in D + vs_addv(D, __ T2D, D, C); + // add 20/21 bits of intermediate product in top of B into low 52-bit result + vs_addv(A, __ T2D, A, B); + // save and then mask off any overflow bit from computing low 52-bit result + vs_ushr(B, __ T2D, A, montMulP256Shift2); + vs_andr(A, A, limb_mask_vec); + // add any remaining carry into the high 52-bit result + vs_addv(D, __ T2D, D, B); + + // the write interleaves the 4 successive pairs of low and + // high results: (l0, l1), (h0, h1), ... (l6, l7), (h6, h7) + vs_st1_interleaved(A, D, mul_ptr); + + // Free mul_ptr + common_regs = common_regs.remaining() + mul_ptr; + mul_ptr = noreg; + + ///////////////////////// + // Loop 2 & 3 + ///////////////////////// + + for (int i = 0; i < 2; i++) { + // Load a_i and increment by 8 bytes + __ ldr(a_i, __ post(a, BytesPerLong)); + __ ldr(c_i, c_ptr); //Load prior c_i + + // Limb 0 + gpr_partial_mult_52(a_i, b_0, high, low, limb_mask); + __ add(low, low, c_i); + __ ldr(c_i, Address(c_ptr, BytesPerLong)); + __ andr(n, low, limb_mask); + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ lsr(tmp, low, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high); + + // Limb 1 + gpr_partial_mult_52(a_i, b_1, high, low, limb_mask); + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 2 * BytesPerLong)); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, c_ptr); + __ add(c_i, tmp, high); + + // Limb 2 + gpr_partial_mult_52(a_i, b_2, high, low, limb_mask); + __ ldr(tmp, Address(c_ptr, 3 * BytesPerLong)); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, BytesPerLong)); + __ add(c_i, tmp, high); + + // Limb 3 + gpr_partial_mult_52(a_i, b_3, high, low, limb_mask); + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 4 * BytesPerLong)); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ add(c_i, tmp, high); + + // Limb 4 + gpr_partial_mult_52(a_i, b_4, high, low, limb_mask); + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ str(high, Address(c_ptr, 4 * BytesPerLong)); + } + // Reallocate regs b_0, b_1, b_2 and b_3 + common_regs = common_regs.remaining() + + b_0 + b_1 + b_2 + b_3; + b_0 = b_1 = b_2 = b_3 = noreg; + + Register low_1 = *common_regs++; + Register high_1 = *common_regs++; + + ////////////////////////////// + // a[3] + ////////////////////////////// + + // For a_3 and a_4 we have already computed the cross-products + // with b_0 ... b_3 and stored them on the stack relative to + // `mul_ptr` i.e. the current `sp`in the order + // l(a_3 * b_0), l(a_3 * b_1), h(a_3 * b_0), h(a_3 * b_1), + // l(a_3 * b_2), l(a_3 * b_3), h(a_3 * b_2), h(a_3 * b_3), + // l(a_4 * b_0), l(a_4 * b_1), h(a_4 * b_0), h(a_4 * b_1), + // l(a_4 * b_2), l(a_4 * b_3), h(a_4 * b_2), h(a_4 * b_3), + // where l(x) is the low 52 bits of x and h(x) is the high 52 bits + + __ ldr(low_1, Address(sp)); + __ ldr(high_1, Address(sp, 2 * BytesPerLong)); + + __ ldr(low, Address(sp, BytesPerLong)); + __ ldr(high, Address(sp, 3 * BytesPerLong)); + __ ldr(a_i, __ post(a, BytesPerLong)); + __ ldr(c_i, c_ptr); + + // Limb 0 + __ add(low_1, low_1, c_i); + __ ldr(c_i, Address(c_ptr, BytesPerLong)); + __ andr(n, low_1, limb_mask); + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ lsr(tmp, low_1, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high_1); + + // Limb 1 + __ ldr(low_1, Address(sp, 4 * BytesPerLong)); + __ ldr(high_1, Address(sp, 6 * BytesPerLong)); + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 2 * BytesPerLong)); + __ andr(mod_low, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, c_ptr); + __ add(c_i, tmp, high); + + // Limb 2 + __ ldr(low, Address(sp, 5 * BytesPerLong)); + __ ldr(high, Address(sp, 7 * BytesPerLong)); + __ ldr(tmp, Address(c_ptr, 3 * BytesPerLong)); + __ add(c_i, c_i, low_1); + __ str(c_i, Address(c_ptr, BytesPerLong)); + __ add(c_i, tmp, high_1); + + // Limb 3 + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 4 * BytesPerLong)); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ add(c_i, tmp, high); + + // Limb 4 + __ ldr(low, Address(sp, 8 * BytesPerLong)); + __ ldr(high, Address(sp, 10 * BytesPerLong)); + gpr_partial_mult_52(a_i, b_4, high_1, low_1, limb_mask); + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ add(c_i, c_i, low_1); + __ str(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ str(high_1, Address(c_ptr, 4 * BytesPerLong)); + + ////////////////////////////// + // a[4] + ////////////////////////////// + + Register c5 = *common_regs++, + c6 = *common_regs++, + c7 = *common_regs++; + + __ ldr(a_i, a); + __ ldr(c_i, c_ptr); + + // Limb 0 + __ ldr(low_1, Address(sp, 9 * BytesPerLong)); + __ ldr(high_1, Address(sp, 11 * BytesPerLong)); + + __ add(low, low, c_i); + __ ldr(c_i, Address(c_ptr, BytesPerLong)); + __ andr(n, low, limb_mask); + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ lsr(tmp, low, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high); + + __ ldr(low, Address(sp, 12 * BytesPerLong)); + __ ldr(high, Address(sp, 14 * BytesPerLong)); + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ add(c5, c_i, low_1); + __ ldr(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ lsr(tmp, c5, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high_1); + + // Limb 2 + __ ldr(low_1, Address(sp, 13 * BytesPerLong)); + __ ldr(high_1, Address(sp, 15 * BytesPerLong)); + __ add(c6, c_i, low); + __ ldr(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ lsr(tmp, c6, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high); + + // Limb 3 + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ add(c7, c_i, low_1); + __ ldr(c_i, Address(c_ptr, 4 * BytesPerLong)); + __ lsr(tmp, c7, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high_1); + + // Limb 4 + gpr_partial_mult_52(a_i, b_4, high, low, limb_mask); + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + + // Reallocate b_4 + common_regs = common_regs.remaining() + b_4; + b_4 = noreg; + + Register c8 = *common_regs++, + c9 = *common_regs++; + + __ add(c8, c_i, low); + __ lsr(c9, c8, montMulP256Shift2); + __ add(c9, c9, high); + + __ andr(c5, c5, limb_mask); + __ andr(c6, c6, limb_mask); + __ andr(c7, c7, limb_mask); + __ andr(c8, c8, limb_mask); + + ///////////////////////////// + // Final carry propagate + ///////////////////////////// + + // c0 = c5 - modulus[0]; + // c1 = c6 - modulus[1] + (c0 >> BITS_PER_LIMB); + // c0 &= LIMB_MASK; + // c2 = c7 + (c1 >> BITS_PER_LIMB); + // c1 &= LIMB_MASK; + // c3 = c8 - modulus[3] + (c2 >> BITS_PER_LIMB); + // c2 &= LIMB_MASK; + // c4 = c9 - modulus4] + (c3 >> BITS_PER_LIMB); + // c3 &= LIMB_MASK; + + // Free up all unused regs + common_regs = common_regs.remaining() + + c_ptr + low + high + mod_high + + mod_low + a_i + c_i + n + low_1 + high_1; + c_ptr = low = high = mod_high + = mod_low = a_i = c_i = n = low_1 = high_1 = noreg; + + Register c0 = *common_regs++, + c1 = *common_regs++, + c2 = *common_regs++, + c3 = *common_regs++, + c4 = *common_regs++; + + __ sub(c0, c5, mod_0); + __ sub(c1, c6, mod_1); + __ sub(c3, c8, mod_3); + __ sub(c4, c9, mod_4); + __ add(c1, c1, c0, Assembler::ASR, montMulP256Shift2); + __ andr(c0, c0, limb_mask); + __ add(c2, c7, c1, Assembler::ASR, montMulP256Shift2); + __ andr(c1, c1, limb_mask); + __ add(c3, c3, c2, Assembler::ASR, montMulP256Shift2); + __ andr(c2, c2, limb_mask); + __ add(c4, c4, c3, Assembler::ASR, montMulP256Shift2); + __ andr(c3, c3, limb_mask); + + // Final write back + // mask = c4 >> 63 + // r[0] = ((c5 & mask) | (c0 & ~mask)); + // r[1] = ((c6 & mask) | (c1 & ~mask)); + // r[2] = ((c7 & mask) | (c2 & ~mask)); + // r[3] = ((c8 & mask) | (c3 & ~mask)); + // r[4] = ((c9 & mask) | (c4 & ~mask)); + + common_regs = common_regs.remaining() + + mod_0 + mod_1 + mod_3 + mod_4; + mod_0 = mod_1 = mod_3 = mod_4 = noreg; + + Register mask = *common_regs++; + Register nmask = *common_regs++; + + __ asr(mask, c4, 63); + __ mvn(nmask, mask); + __ andr(c5, c5, mask); + __ andr(tmp, c0, nmask); + __ orr(c5, c5, tmp); + __ andr(c6, c6, mask); + __ andr(tmp, c1, nmask); + __ orr(c6, c6, tmp); + __ andr(c7, c7, mask); + __ andr(tmp, c2, nmask); + __ orr(c7, c7, tmp); + __ andr(c8, c8, mask); + __ andr(tmp, c3, nmask); + __ orr(c8, c8, tmp); + __ andr(c9, c9, mask); + __ andr(tmp, c4, nmask); + __ orr(c9, c9, tmp); + + __ str(c5, result); + __ str(c6, Address(result, BytesPerLong)); + __ str(c7, Address(result, 2 * BytesPerLong)); + __ str(c8, Address(result, 3 * BytesPerLong)); + __ str(c9, Address(result, 4 * BytesPerLong)); + + // End intrinsic call + __ add(sp, sp, cDataSize + mulDataSize); + __ pop(callee_saved, sp); + __ leave(); __ mov(r0, zr); // return 0 __ ret(lr); @@ -7673,6 +8327,455 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_intpoly_assign() { + // KNOWN Lengths: + // MontgomeryIntPolynP256: 5 = 4 + 1 + // IntegerPolynomial1305: 5 = 4 + 1 + // IntegerPolynomial25519: 10 = 8 + 2 + // IntegerPolynomialP256: 10 = 8 + 2 + // Curve25519OrderField: 10 = 8 + 2 + // Curve25519OrderField: 10 = 8 + 2 + // P256OrderField: 10 = 8 + 2 + // IntegerPolynomialP384: 14 = 8 + 4 + 2 + // P384OrderField: 14 = 8 + 4 + 2 + // IntegerPolynomial448: 16 = 8 + 8 + // Curve448OrderField: 16 = 8 + 8 + // Curve448OrderField: 16 = 8 + 8 + // IntegerPolynomialP521: 19 = 8 + 8 + 2 + 1 + // P521OrderField: 19 = 8 + 8 + 2 + 1 + // Special Cases 5, 10, 14, 16, 19 + assert(UseIntPolyIntrinsics, "what are we doing here?"); + 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); + start = __ pc(); + __ enter(); + + // Inputs + const Register set = c_rarg0; + const Register aLimbs = c_rarg1; + const Register bLimbs = c_rarg2; + const Register length = c_rarg3; + + Label L_Length5, L_Length10, L_Length14, L_Length16, L_Length19, L_Default, L_Done; + + /* + int maskValue = -set; + for (int i = 0; i < a.length; i++) { + long dummyLimbs = maskValue & (a[i] ^ b[i]); + a[i] = dummyLimbs ^ a[i]; + } + */ + Register mask_scalar = r4; + FloatRegister mask_vec = v0; + + __ neg(mask_scalar, set); + __ dup(mask_vec, __ T2D, mask_scalar); + + __ cmp(length, (u1)5); + __ br(Assembler::EQ, L_Length5); + __ cmp(length, (u1)10); + __ br(Assembler::EQ, L_Length10); + __ cmp(length, (u1)14); + __ br(Assembler::EQ, L_Length14); + __ cmp(length, (u1)16); + __ br(Assembler::EQ, L_Length16); + __ cmp(length, (u1)19); + __ br(Assembler::EQ, L_Length19); + __ b(L_Default); + + + // Length = 5 + // Use 5 GPRs (neon not faster with this few limbs) + __ BIND(L_Length5); + { + Register a0 = r5; + Register a1 = r6; + Register a2 = r7; + Register a3 = r10; + Register a4 = r11; + Register b0 = r12; + Register b1 = r13; + Register b2 = r14; + Register b3 = r15; + Register b4 = r19; + + __ push(r19, sp); + + __ ldr(a0, aLimbs); + __ ldr(a1, Address(aLimbs, 1 * BytesPerLong)); + __ ldr(a2, Address(aLimbs, 2 * BytesPerLong)); + __ ldr(a3, Address(aLimbs, 3 * BytesPerLong)); + __ ldr(a4, Address(aLimbs, 4 * BytesPerLong)); + + __ ldr(b0, bLimbs); + __ ldr(b1, Address(bLimbs, 1 * BytesPerLong)); + __ ldr(b2, Address(bLimbs, 2 * BytesPerLong)); + __ ldr(b3, Address(bLimbs, 3 * BytesPerLong)); + __ ldr(b4, Address(bLimbs, 4 * BytesPerLong)); + + __ eor(b0, b0, a0); + __ eor(b1, b1, a1); + __ eor(b2, b2, a2); + __ eor(b3, b3, a3); + __ eor(b4, b4, a4); + + __ andr(b0, b0, mask_scalar); + __ andr(b1, b1, mask_scalar); + __ andr(b2, b2, mask_scalar); + __ andr(b3, b3, mask_scalar); + __ andr(b4, b4, mask_scalar); + + __ eor(a0, a0, b0); + __ eor(a1, a1, b1); + __ eor(a2, a2, b2); + __ eor(a3, a3, b3); + __ eor(a4, a4, b4); + + __ str(a0, aLimbs); + __ str(a1, Address(aLimbs, 1 * BytesPerLong)); + __ str(a2, Address(aLimbs, 2 * BytesPerLong)); + __ str(a3, Address(aLimbs, 3 * BytesPerLong)); + __ str(a4, Address(aLimbs, 4 * BytesPerLong)); + + __ pop(r19, sp); + __ b(L_Done); + } + + // Length = 10 + // Split into 4 neon regs and 2 GPRs + __ BIND(L_Length10); + { + Register a9 = r10; + Register a10 = r11; + Register b9 = r12; + Register b10 = r13; + + VSeq<4> a_vec(16); + VSeq<4> b_vec(20); + + __ ldr(a9, Address(aLimbs, 8 * BytesPerLong)); + __ ldr(a10, Address(aLimbs, 9 * BytesPerLong)); + __ ldr(b9, Address(bLimbs, 8 * BytesPerLong)); + __ ldr(b10, Address(bLimbs, 9 * BytesPerLong)); + + vs_ldpq(a_vec, aLimbs); + + __ eor(b9, b9, a9); + __ eor(b10, b10, a10); + + vs_ldpq(b_vec, bLimbs); + + __ andr(b9, b9, mask_scalar); + __ andr(b10, b10, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a9, a9, b9); + __ eor(a10, a10, b10); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a9, Address(aLimbs, 8 * BytesPerLong)); + __ str(a10, Address(aLimbs, 9 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + vs_stpq_post(a_vec, aLimbs); + + __ b(L_Done); + } + + // Length = 14 + // Split into 5 neon regs and 4 GPRs + __ BIND(L_Length14); + { + Register a10 = r5; + Register a11 = r6; + Register a12 = r7; + Register a13 = r8; + Register b10 = r9; + Register b11 = r10; + Register b12 = r11; + Register b13 = r12; + + VSeq<5> a_vec(16); + VSeq<5> b_vec(22); + + int offsets[2] = { 0, 32 }; + + __ ldr(a10, Address(aLimbs, 10 * BytesPerLong)); + __ ldr(a11, Address(aLimbs, 11 * BytesPerLong)); + __ ldr(a12, Address(aLimbs, 12 * BytesPerLong)); + __ ldr(a13, Address(aLimbs, 13 * BytesPerLong)); + + __ ldr(b10, Address(bLimbs, 10 * BytesPerLong)); + __ ldr(b11, Address(bLimbs, 11 * BytesPerLong)); + __ ldr(b12, Address(bLimbs, 12 * BytesPerLong)); + __ ldr(b13, Address(bLimbs, 13 * BytesPerLong)); + + __ ld1(a_vec[0], __ T2D, aLimbs); + vs_ldpq_indexed(vs_tail(a_vec), aLimbs, 16, offsets); + + __ eor(b10, b10, a10); + __ eor(b11, b11, a11); + __ eor(b12, b12, a12); + __ eor(b13, b13, a13); + + __ ld1(b_vec[0], __ T2D, bLimbs); + vs_ldpq_indexed(vs_tail(b_vec), bLimbs, 16, offsets); + + __ andr(b10, b10, mask_scalar); + __ andr(b11, b11, mask_scalar); + __ andr(b12, b12, mask_scalar); + __ andr(b13, b13, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a10, a10, b10); + __ eor(a11, a11, b11); + __ eor(a12, a12, b12); + __ eor(a13, a13, b13); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a10, Address(aLimbs, 10 * BytesPerLong)); + __ str(a11, Address(aLimbs, 11 * BytesPerLong)); + __ str(a12, Address(aLimbs, 12 * BytesPerLong)); + __ str(a13, Address(aLimbs, 13 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + + __ st1(a_vec[0], __ T2D, aLimbs); + vs_stpq_indexed(vs_tail(a_vec), aLimbs, 16, offsets); + + __ b(L_Done); + } + + // Length = 16 + // Use 8 neon regs + __ BIND(L_Length16); + { + VSeq<8> a_vec(16); + VSeq<8> b_vec(24); + + vs_ldpq(a_vec, aLimbs); + vs_ldpq(b_vec, bLimbs); + vs_eor(b_vec, b_vec, a_vec); + vs_andr(b_vec, b_vec, mask_vec); + vs_eor(a_vec, a_vec, b_vec); + vs_stpq_post(a_vec, aLimbs); + + __ b(L_Done); + } + + // Length = 19 + // Split into 8 neon regs and 3 GPRs + __ BIND(L_Length19); + { + Register a17 = r10; + Register a18 = r11; + Register a19 = r12; + Register b17 = r13; + Register b18 = r14; + Register b19 = r15; + + VSeq<8> a_vec(16); + VSeq<8> b_vec(24); + + __ ldr(a17, Address(aLimbs, 16 * BytesPerLong)); + __ ldr(a18, Address(aLimbs, 17 * BytesPerLong)); + __ ldr(a19, Address(aLimbs, 18 * BytesPerLong)); + __ ldr(b17, Address(bLimbs, 16 * BytesPerLong)); + __ ldr(b18, Address(bLimbs, 17 * BytesPerLong)); + __ ldr(b19, Address(bLimbs, 18 * BytesPerLong)); + + vs_ldpq(a_vec, aLimbs); + + __ eor(b17, b17, a17); + __ eor(b18, b18, a18); + __ eor(b19, b19, a19); + + vs_ldpq(b_vec, bLimbs); + + __ andr(b17, b17, mask_scalar); + __ andr(b18, b18, mask_scalar); + __ andr(b19, b19, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a17, a17, b17); + __ eor(a18, a18, b18); + __ eor(a19, a19, b19); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a17, Address(aLimbs, 16 * BytesPerLong)); + __ str(a18, Address(aLimbs, 17 * BytesPerLong)); + __ str(a19, Address(aLimbs, 18 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + vs_stpq_post(a_vec, aLimbs); + + __ b(L_Done); + } + + __ BIND(L_Default); + { + Register ctr = r5; + Register a_val = r6; + Register b_val = r7; + + __ mov(ctr, length); // length (the number of limbs) is never 0 + + Label default_loop; + __ BIND(default_loop); + + __ ldr(a_val, aLimbs); + __ ldr(b_val, __ post(bLimbs, 8)); + __ eor(b_val, b_val, a_val); + __ andr(b_val, b_val, mask_scalar); + __ eor(a_val, a_val, b_val); + __ str(a_val, __ post(aLimbs, 8)); + __ sub(ctr, ctr, 1); + __ cmp(ctr, (u1)0); + __ br(Assembler::NE, default_loop); + } + + __ BIND(L_Done); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ mov(r0, zr); // return 0 + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + + /** + * Arithmetic polynomial multiplication in Curve25519. The algorithm mimics + * the version in the IntegerPolynomial25519 class, including the use of all + * columns (no folding method). + * + * Arguments: + * + * Inputs: + * c_rarg0 - long[] aLimbs + * c_rarg1 - long[] bLimbs + * + * Output: + * c_rarg2 - long[] rLimbs result + */ + address generate_intpoly_mult_25519() { + StubId stub_id = StubId::stubgen_intpoly_mult_25519_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(); + __ enter(); + + // Register Map + const Register aLimbs = c_rarg0; // r0 + const Register bLimbs = c_rarg1; // r1 + const Register rLimbs = c_rarg2; // r2 + + Register c[] = {r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}; + Register a = r13; + Register b = r14; + Register term = r15; + Register low = r16; + Register high = r17; + + const int32_t limbs = 5; + const int32_t bpl = 51; + const int32_t rem = 64 - bpl; + const int32_t TERM = 19; + const int32_t columns = limbs * 2; + const uint64_t mask = (uint64_t) -1 >> rem; + const uint64_t CARRY_ADD = (uint64_t) 1 << (bpl - 1); + + __ mov(term, TERM); + for (int i = 0; i < columns; i++) { + __ mov(c[i], zr); + } + + // Perform high/low multiplication with signed 5x51 bit limbs + for (int i = 0; i < limbs; i++) { + __ ldr(b, Address(bLimbs, i * 8)); + for (int j = 0; j < limbs; j++) { + __ ldr(a, Address(aLimbs, j * 8)); + __ smulh(high, a, b); + __ mul(low, a, b); + __ extr(high, high, low, bpl); + __ andr(low, low, mask); + __ add(c[i + j], c[i + j], low); + __ add(c[i + j + 1], c[i + j + 1], high); + } + } + + for (int i = 0; i < limbs; i++) { + __ mul(c[i + 5], c[i + 5], term); + __ add(c[i], c[i], c[i + 5]); + } + + // Carry-add with reduction from high limb + Register tmp = low; + Register carry_add = high; + __ mov(carry_add, CARRY_ADD); + + // Limb 3 + __ add(tmp, c[3], carry_add); + __ asr(tmp, tmp, bpl); + __ add(c[4], c[4], tmp); + __ lsl(tmp, tmp, bpl); + __ sub(c[3], c[3], tmp); + + // Limb 4 + __ add(tmp, c[4], carry_add); + __ asr(tmp, tmp, bpl); + + // Reduce high order limb and fold back into low order limb + __ mul(term, tmp, term); + __ add(c[0], c[0], term); + + __ lsl(tmp, tmp, bpl); + __ sub(c[4], c[4], tmp); + + // Limbs 0 - 3 + for (int i = 0; i < (limbs - 1); i++) { + __ add(tmp, c[i], carry_add); + __ asr(tmp, tmp, bpl); + __ add(c[i + 1], c[i + 1], tmp); + __ lsl(tmp, tmp, bpl); + __ sub(c[i], c[i], tmp); + } + + for (int i = 0; i < limbs; i++) { + __ str(c[i], Address(rLimbs, i * 8)); + } + + __ mov(r0, 0); + __ 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; + } + void bcax5(Register a0, Register a1, Register a2, Register a3, Register a4, Register tmp0, Register tmp1, Register tmp2) { __ bic(tmp0, a2, a1); // for a0 @@ -12733,6 +13836,11 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_chacha20Block = generate_chacha20Block_blockpar(); } + if (UseIntPolyIntrinsics) { + StubRoutines::_intpoly_montgomeryMult_P256 = generate_intpoly_montgomeryMult_P256(); + StubRoutines::_intpoly_assign = generate_intpoly_assign(); + } + if (UseKyberIntrinsics) { StubRoutines::_kyberNtt = generate_kyberNtt(); StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt(); @@ -12805,6 +13913,15 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_poly1305_processBlocks = generate_poly1305_processBlocks(); } + // The difference between AArch64 vs. x86_64 intrinsics implementation + // include the lack of square() intrinsics; usage caused a 3.3% performance + // degradation due to the efficiencies of the symmetric squaring shape in + // Java vs. the inefficiencies of the leaf calls and the additional cycles + // required for 64 bit multiplication in AArch64. + if (UseIntPoly25519Intrinsics) { + StubRoutines::_intpoly_mult_25519 = generate_intpoly_mult_25519(); + } + // generate Adler32 intrinsics code if (UseAdler32Intrinsics) { StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); @@ -12845,6 +13962,7 @@ class StubGenerator: public StubCodeGenerator { ADD(_sha512_round_consts); ADD(_sha3_round_consts); ADD(_double_keccak_round_consts); + ADD(_modulus_P256); ADD(_encodeBlock_toBase64); ADD(_encodeBlock_toBase64URL); ADD(_decodeBlock_fromBase64ForNoSIMD); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index b6cf58d6062..a0ce1d04317 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -1123,9 +1123,9 @@ void TemplateTable::aastore() { __ cbz(r0, is_null); // Move subklass into r1 - __ load_klass(r1, r0); + __ load_klass(r1, r0, rscratch1); // Move superklass into r0 - __ load_klass(r0, r3); + __ load_klass(r0, r3, rscratch1); __ ldr(r0, Address(r0, ObjArrayKlass::element_klass_offset())); // Compress array + index*oopSize + 12 into a single register. Frees r2. @@ -1173,7 +1173,7 @@ void TemplateTable::bastore() // Need to check whether array is boolean or byte // since both types share the bastore bytecode. - __ load_klass(r2, r3); + __ load_klass(r2, r3, rscratch1); __ ldrw(r2, Address(r2, Klass::layout_helper_offset())); int diffbit_index = exact_log2(Klass::layout_helper_boolean_diffbit()); Label L_skip; @@ -2194,7 +2194,7 @@ void TemplateTable::_return(TosState state) assert(state == vtos, "only valid state"); __ ldr(c_rarg1, aaddress(0)); - __ load_klass(r3, c_rarg1); + __ load_klass(r3, c_rarg1, rscratch1); __ ldrb(r3, Address(r3, Klass::misc_flags_offset())); Label skip_register_finalizer; __ tbz(r3, exact_log2(KlassFlags::_misc_has_finalizer), skip_register_finalizer); @@ -3338,8 +3338,8 @@ void TemplateTable::invokevirtual_helper(Register index, Register recv, Register flags) { - // Uses temporary registers r0, r3 - assert_different_registers(index, recv, r0, r3); + // Uses temporary registers r0, r3, rscratch1 + assert_different_registers(index, recv, r0, r3, rscratch1); // Test for an invoke of a final method Label notFinal; __ tbz(flags, ResolvedMethodEntry::is_vfinal_shift, notFinal); @@ -3363,7 +3363,7 @@ void TemplateTable::invokevirtual_helper(Register index, __ bind(notFinal); // get receiver klass - __ load_klass(r0, recv); + __ load_klass(r0, recv, rscratch1); // profile this call __ profile_virtual_call(r0, rlocals); @@ -3464,7 +3464,7 @@ void TemplateTable::invokeinterface(int byte_no) { __ tbz(r3, ResolvedMethodEntry::is_vfinal_shift, notVFinal); // Get receiver klass into r3 - __ load_klass(r3, r2); + __ load_klass(r3, r2, rscratch1); Label subtype; __ check_klass_subtype(r3, r0, r4, subtype); @@ -3479,7 +3479,7 @@ void TemplateTable::invokeinterface(int byte_no) { __ bind(notVFinal); // Get receiver klass into r3 - __ load_klass(r3, r2); + __ load_klass(r3, r2, rscratch1); Label no_such_method; @@ -3678,7 +3678,7 @@ void TemplateTable::_new() { __ mov(rscratch1, (intptr_t)markWord::prototype().value()); __ str(rscratch1, Address(r0, oopDesc::mark_offset_in_bytes())); __ store_klass_gap(r0, zr); // zero klass gap for compressed oops - __ store_klass(r0, r4); // store klass last + __ store_klass(r0, r4, rscratch1); // store klass last } if (DTraceAllocProbes) { @@ -3759,7 +3759,7 @@ void TemplateTable::checkcast() __ load_resolved_klass_at_offset(r2, r19, r0, rscratch1); // r0 = klass __ bind(resolved); - __ load_klass(r19, r3); + __ load_klass(r19, r3, rscratch1); // Generate subtype check. Blows r2, r5. Object in r3. // Superklass in r0. Subklass in r19. @@ -3805,12 +3805,12 @@ void TemplateTable::instanceof() { __ get_vm_result_metadata(r0, rthread); __ pop(r3); // restore receiver __ verify_oop(r3); - __ load_klass(r3, r3); + __ load_klass(r3, r3, rscratch1); __ b(resolved); // Get superklass in r0 and subklass in r3 __ bind(quicked); - __ load_klass(r3, r0); + __ load_klass(r3, r0, rscratch1); __ load_resolved_klass_at_offset(r2, r19, r0, rscratch1); __ bind(resolved); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index d1cf8b6feed..5462ccf2a76 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -454,6 +454,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); } + if (FLAG_IS_DEFAULT(UseIntPolyIntrinsics)) { + UseIntPolyIntrinsics = true; + } + if (supports_feature(CPU_ASIMD)) { if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { UseKyberIntrinsics = true; @@ -657,6 +661,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } + if (FLAG_IS_DEFAULT(UseIntPoly25519Intrinsics)) { + FLAG_SET_DEFAULT(UseIntPoly25519Intrinsics, true); + } + if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); } diff --git a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp index 714904ab3df..1b7820fc337 100644 --- a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp @@ -79,7 +79,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { // get receiver klass address npe_addr = __ pc(); - __ load_klass(r16, j_rarg0); + __ load_klass(r16, j_rarg0, rscratch1); #ifndef PRODUCT if (DebugVtables) { @@ -189,7 +189,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // get receiver klass (also an implicit null-check) address npe_addr = __ pc(); - __ load_klass(recv_klass_reg, j_rarg0); + __ load_klass(recv_klass_reg, j_rarg0, rscratch1); // Receiver subtype check against REFC. // Get selected method from declaring class and itable index diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 45ae283e05a..7ae3381600e 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1112,26 +1112,26 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 30 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/arm/frame_arm.hpp b/src/hotspot/cpu/arm/frame_arm.hpp index 6d4ac042831..8ca7a555b93 100644 --- a/src/hotspot/cpu/arm/frame_arm.hpp +++ b/src/hotspot/cpu/arm/frame_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 @@ -27,7 +27,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, @@ -61,6 +60,7 @@ // between a callee frame and its stack arguments, where it is part // of the caller/callee overlap metadata_words_at_top = 0, + // in bytes frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 @@ -122,6 +122,4 @@ // helper to update a map with callee-saved FP static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr); - static jint interpreter_frame_expression_stack_direction() { return -1; } - #endif // CPU_ARM_FRAME_ARM_HPP diff --git a/src/hotspot/cpu/ppc/assembler_ppc.cpp b/src/hotspot/cpu/ppc/assembler_ppc.cpp index ab16fc437e9..406d0b446a4 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.cpp @@ -75,23 +75,46 @@ int Assembler::branch_destination(int inst, int pos) { return r; } -// Low-level andi-one-instruction-macro. -void Assembler::andi(Register a, Register s, const long ui16) { - if (is_power_of_2(((unsigned long) ui16)+1)) { +// Low-level andi-one-instruction-macro. May clobber CR0. +void Assembler::andi(Register a, Register s, julong int_or_long_const) { + // Instructions which don't set CR0 are preferred. + if (int_or_long_const == 0) { + // should not be handled as pow2minus1 + li(a, 0); + } else if (is_power_of_2(int_or_long_const + 1)) { // pow2minus1 - clrldi(a, s, 64 - log2i_exact((((unsigned long) ui16)+1))); - } else if (is_power_of_2((jlong) ui16)) { - // pow2 - rlwinm(a, s, 0, 31 - log2i_exact((jlong) ui16), 31 - log2i_exact((jlong) ui16)); - } else if (is_power_of_2((jlong)-ui16)) { - // negpow2 - clrrdi(a, s, log2i_exact((jlong)-ui16)); + clrldi(a, s, 64 - log2i_exact(int_or_long_const + 1)); + } else if (is_power_of_2(-int_or_long_const)) { + // negpow2 (includes (julong)min_jlong) + clrrdi(a, s, log2i_exact(-int_or_long_const)); + } else if (is_uimm((jlong)int_or_long_const, 32) && has_consecutive_ones(int_or_long_const)) { + // consecutive ones + rlwinm(a, s, 0, count_leading_zeros((uint32_t)int_or_long_const), + 31 - count_trailing_zeros((uint32_t)int_or_long_const)); + } else if (is_uimm((jlong)int_or_long_const, 16)) { + // side effect: clobbers CR0 + andi_(a, s, int_or_long_const); } else { - assert(is_uimm(ui16, 16), "must be 16-bit unsigned immediate"); - andi_(a, s, ui16); + assert(is_uimm((jlong)int_or_long_const, 32) && (int_or_long_const & 0xFFFF) == 0, + "not encodable: " UINT64_FORMAT_X, int_or_long_const); + // side effect: clobbers CR0 + andis_(a, s, int_or_long_const >> 16); } } +// Check if int_or_long_const is supported by Assembler::andi. +bool Assembler::andi_supports(julong int_or_long_const) { + // 16 bit always possible by andi_ (but other instructions are preferred) + if (is_uimm((jlong)int_or_long_const, 16)) return true; + + // special cases 32 bit: higher 16 bit and consecutive ones are supported + if (is_uimm((jlong)int_or_long_const, 32) && + ((int_or_long_const & 0xFFFF) == 0 || has_consecutive_ones(int_or_long_const))) return true; + + // special cases 64 bit: clrldi, clrrdi + return is_power_of_2(int_or_long_const + 1) || is_power_of_2(-int_or_long_const); +} + // RegisterOrConstant version. void Assembler::ld(Register d, RegisterOrConstant roc, Register s1) { if (roc.is_constant()) { diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index f62c93e466c..77c7f63cd06 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1048,6 +1048,13 @@ class Assembler : public AbstractAssembler { return (julong)x < maxplus1; } + // Test if x has exactly one consecutive range of one bits (e.g. 00111000) + static bool has_consecutive_ones(julong x) { + if (x == max_julong) return true; + if (x == 0) return false; + return is_power_of_2((x >> count_trailing_zeros(x)) + 1); + } + protected: // helpers @@ -1606,7 +1613,8 @@ class Assembler : public AbstractAssembler { inline void isel_0( Register d, ConditionRegister cr, Condition cc, Register b = noreg); // PPC 1, section 3.3.11, Fixed-Point Logical Instructions - void andi( Register a, Register s, long ui16); // optimized version + void andi( Register a, Register s, julong int_or_long_const); // optimized version, may clobber CR0 + static bool andi_supports(julong int_or_long_const); inline void andi_( Register a, Register s, int ui16); inline void andis_( Register a, Register s, int ui16); inline void ori( Register a, Register s, int ui16); diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 1270471d150..1ec710aad29 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -1669,26 +1669,40 @@ void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr d = dest->as_register_lo(); l = left->as_register_lo(); } - long uimms = (unsigned long)uimm >> 16, - uimmss = (unsigned long)uimm >> 32; + long uimms = (unsigned long)uimm >> 16; switch (code) { case lir_logic_and: - if (uimmss != 0 || (uimms != 0 && (uimm & 0xFFFF) != 0) || is_power_of_2(uimm)) { - __ andi(d, l, uimm); // special cases - } else if (uimms != 0) { __ andis_(d, l, uimms); } - else { __ andi_(d, l, uimm); } + if (Assembler::andi_supports(uimm)) { + __ andi(d, l, uimm); // includes andis_ and special cases + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ andr(d, l, R0); + } break; case lir_logic_or: - if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ oris(d, l, uimms); } - else { __ ori(d, l, uimm); } + if (Assembler::is_uimm(uimm, 16)) { + __ ori(d, l, uimm); + } else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) { + __ oris(d, l, uimms); + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ orr(d, l, R0); + } break; case lir_logic_xor: - if (uimm == -1) { __ nand(d, l, l); } // special case - else if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ xoris(d, l, uimms); } - else { __ xori(d, l, uimm); } + if (Assembler::is_uimm(uimm, 16)) { + __ xori(d, l, uimm); + } else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) { + __ xoris(d, l, uimms); + } else if (uimm == -1) { + __ nand(d, l, l); // special case + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ xorr(d, l, R0); + } break; default: ShouldNotReachHere(); @@ -2717,7 +2731,13 @@ void LIR_Assembler::membar_storeload() { } void LIR_Assembler::on_spin_wait() { - Unimplemented(); + // SMT priority hint: drop to low for the spin, then restore to medium so + // subsequent code is not penalised. + // Yield (or 27,27,27) is not used because it was never implemented on Power CPUs, see JDK-8201218. + __ block_comment("spin_wait {"); + __ smt_prio_low(); + __ smt_prio_medium(); + __ block_comment("}"); } void LIR_Assembler::leal(LIR_Opr addr_opr, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info) { diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index a652a155f62..56c069053c6 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -578,18 +578,13 @@ inline bool can_handle_logic_op_as_uimm(ValueType *type, Bytecodes::Code bc) { Assembler::is_uimm((jlong)((julong)int_or_long_const >> 16), 16)) return true; // see Assembler::andi - if (bc == Bytecodes::_iand && - (is_power_of_2(int_or_long_const+1) || - is_power_of_2(int_or_long_const) || - is_power_of_2(-int_or_long_const))) return true; - if (bc == Bytecodes::_land && - (is_power_of_2((unsigned long)int_or_long_const+1) || - (Assembler::is_uimm(int_or_long_const, 32) && is_power_of_2(int_or_long_const)) || - (int_or_long_const != min_jlong && is_power_of_2(-int_or_long_const)))) return true; + if ((bc == Bytecodes::_iand || bc == Bytecodes::_land)) + return Assembler::andi_supports(int_or_long_const); // special case: xor -1 - if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor) && - int_or_long_const == -1) return true; + if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor)) + return (int_or_long_const == -1); + return false; } diff --git a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp index 09efa2c841b..5649f23856c 100644 --- a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 1999, 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 @@ -760,22 +760,9 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { break; case StubId::c1_dtrace_object_alloc_id: - { // O0: object + { __ unimplemented("stub dtrace_object_alloc_id"); __ set_info("dtrace_object_alloc", dont_gc_arguments); -// // We can't gc here so skip the oopmap but make sure that all -// // the live registers get saved. -// save_live_registers(sasm); -// -// __ save_thread(L7_thread_cache); -// __ call(CAST_FROM_FN_PTR(address, static_cast(SharedRuntime::dtrace_object_alloc)), -// relocInfo::runtime_call_type); -// __ delayed()->mov(I0, O0); -// __ restore_thread(L7_thread_cache); -// -// restore_live_registers(sasm); -// __ ret(); -// __ delayed()->restore(); } break; diff --git a/src/hotspot/cpu/ppc/frame_ppc.hpp b/src/hotspot/cpu/ppc/frame_ppc.hpp index ebe5d24c072..43d5fd41068 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.hpp @@ -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) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -391,8 +391,6 @@ } enum { - // normal return address is 1 bundle past PC - pc_return_offset = 0, // size, in words, of frame metadata (e.g. pc and link) metadata_words = sizeof(java_abi) >> LogBytesPerWord, // size, in words, of metadata at frame bottom, i.e. it is not part of the @@ -402,16 +400,13 @@ // between a callee frame and its stack arguments, where it is part // of the caller/callee overlap metadata_words_at_top = sizeof(java_abi) >> LogBytesPerWord, - // size, in words, of frame metadata at the frame top that needs - // to be reserved for callee functions in the runtime + // in bytes frame_alignment = 16, frame_alignment_in_words = frame_alignment >> LogBytesPerWord, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 }; - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 582327282fd..7dbb0182266 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -56,10 +56,11 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler *masm, Register base, RegisterOrConstant ind_or_offs, Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ShenandoahSATBBarrier) { __ block_comment("satb_barrier (shenandoahgc) {"); - satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level, extra_stack_space); __ block_comment("} satb_barrier (shenandoahgc)"); } } @@ -68,10 +69,11 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ShenandoahLoadRefBarrier) { __ block_comment("load_reference_barrier (shenandoahgc) {"); - load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level); + load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level, extra_stack_space); __ block_comment("} load_reference_barrier (shenandoahgc)"); } } @@ -205,7 +207,8 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco Register base, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { assert(ShenandoahSATBBarrier, "Should be checked by caller"); assert_different_registers(tmp1, tmp2, pre_val, noreg); @@ -299,7 +302,7 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco if (preserve_gp_registers) { nbytes_save = (preserve_fp_registers ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs - : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord + extra_stack_space; __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers); } @@ -343,7 +346,8 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_impl( Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ind_or_offs.is_register()) { assert_different_registers(tmp1, tmp2, base, ind_or_offs.as_register(), dst, noreg); } else { @@ -430,7 +434,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_impl( if (preserve_gp_registers) { nbytes_save = (preserve_fp_registers ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs - : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord + extra_stack_space; __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers); } @@ -655,6 +659,26 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ block_comment("} try_peek_weak_handle_in_nmethod (shenandoahgc)"); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler *masm, Register obj, const char* msg) { + if (!VerifyOops) { + return; + } + + __ mr(R0, obj); + + // This routine is sometimes called before applying GC barriers. + // With +COH, verification can touch the klass that may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + __ lbz(R0, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread); + __ andi_(R0, R0, ShenandoahHeap::HAS_FORWARDED); + __ bne(CR0, L_skip); + } + + __ verify_oop(R0, msg); + __ bind(L_skip); +} + void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register preserve) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); @@ -693,243 +717,119 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler *ce, ShenandoahPreBarrierStub *stub) { - __ block_comment("gen_pre_barrier_stub (shenandoahgc) {"); - - ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { + __ block_comment("keepalive_barrier_stub (shenandoahgc) {"); __ bind(*stub->entry()); - // GC status has already been verified by 'ShenandoahBarrierSetC1::pre_barrier'. - // This stub is the slowpath of that function. + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); - assert(stub->pre_val()->is_register(), "pre_val must be a register"); - Register pre_val = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); - // If 'do_load()' returns false, the to-be-stored value is already available in 'stub->pre_val()' - // ("preloaded mode" of the store barrier). + // If 'do_load()' returns false, the to-be-stored value is already available in 'obj' if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, false); } - // Fast path: Reference is null. - __ cmpdi(CR0, pre_val, 0); + // Fast path: reference is null. + __ cmpdi(CR0, obj, 0); __ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CR0, Assembler::equal), *stub->continuation()); // Argument passing via the stack. - __ std(pre_val, -8, R1_SP); + __ std(obj, -8, R1_SP); - __ load_const_optimized(R0, bs->pre_barrier_c1_runtime_code_blob()->code_begin()); + address blob_addr = bs->keepalive_barrier_stub(); + __ load_const_optimized(R0, blob_addr); __ call_stub(R0); __ b(*stub->continuation()); - __ block_comment("} gen_pre_barrier_stub (shenandoahgc)"); + __ block_comment("} keepalive_barrier_stub (shenandoahgc)"); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler *ce, - ShenandoahLoadReferenceBarrierStub *stub) { - __ block_comment("gen_load_reference_barrier_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { + __ block_comment("load_reference_barrier_stub (shenandoahgc) {"); - ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); __ bind(*stub->entry()); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); + Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - assert_different_registers(addr, res, tmp1, tmp2); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == R3_RET, "C1 must know about our slow call result register"); - assert(R3_RET == res, "res must be r3"); + // Argument passing via the stack. + __ std(obj, -8, R1_SP); + __ std(addr, -16, R1_SP); - if (res != obj) { - __ mr(res, obj); + address blob_addr = bs->load_reference_barrier_stub(stub->decorators()); + __ load_const_optimized(R0, blob_addr); + __ call_stub(R0); + if (obj != slow_result) { + __ mr(obj, slow_result); } - DecoratorSet decorators = stub->decorators(); - - /* ==== Check whether region is in collection set ==== */ - // GC status (unstable) has already been verified by 'ShenandoahBarrierSetC1::load_reference_barrier_impl'. - // This stub is the slowpath of that function. - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - if (is_strong) { - // Check whether object is in collection set. - __ load_const_optimized(tmp2, ShenandoahHeap::in_cset_fast_test_addr(), tmp1); - __ srdi(tmp1, obj, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ lbzx(tmp2, tmp1, tmp2); - - __ andi_(tmp2, tmp2, 1); - __ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CR0, Assembler::equal), *stub->continuation()); - } - - address blob_addr = nullptr; - - if (is_strong) { - if (is_native) { - blob_addr = bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin(); - } else { - blob_addr = bs->load_reference_barrier_strong_rt_code_blob()->code_begin(); - } - } else if (is_weak) { - blob_addr = bs->load_reference_barrier_weak_rt_code_blob()->code_begin(); - } else { - assert(is_phantom, "only remaining strength"); - blob_addr = bs->load_reference_barrier_phantom_rt_code_blob()->code_begin(); - } - - assert(blob_addr != nullptr, "code blob cannot be found"); - - // Argument passing via the stack. 'obj' is passed implicitly (as asserted above). - __ std(addr, -8, R1_SP); - - __ load_const_optimized(tmp1, blob_addr, tmp2); - __ call_stub(tmp1); - - // 'res' is 'R3_RET'. The result is thus already in the correct register. - __ b(*stub->continuation()); - __ block_comment("} gen_load_reference_barrier_stub (shenandoahgc)"); + __ block_comment("} load_reference_barrier_stub (shenandoahgc)"); } #undef __ #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler *sasm) { - __ block_comment("generate_c1_pre_barrier_runtime_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ block_comment("keepalive_barrier_runtime_stub (shenandoahgc) {"); - Label runtime, skip_barrier; - BarrierSet *bs = BarrierSet::barrier_set(); + Register obj = R3_ARG1; + Register tmp1 = R11_scratch1; + Register tmp2 = R12_scratch2; - // Argument passing via the stack. - const int caller_stack_slots = 3; + // Save registers we are about to clobber + __ std(obj, -16, R1_SP); + __ std(tmp1, -24, R1_SP); + __ std(tmp2, -32, R1_SP); - Register R0_pre_val = R0; - __ ld(R0, -8, R1_SP); - Register R11_tmp1 = R11_scratch1; - __ std(R11_tmp1, -16, R1_SP); - Register R12_tmp2 = R12_scratch2; - __ std(R12_tmp2, -24, R1_SP); + // Pull the arguments from stack + __ ld(obj, -8, R1_SP); - /* ==== Check whether marking is active ==== */ - // Even though gc status was checked in 'ShenandoahBarrierSetAssembler::gen_pre_barrier_stub', - // another check is required as a safepoint might have been reached in the meantime (JDK-8140588). - __ lbz(R12_tmp2, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread); + satb_barrier(sasm, noreg, noreg, obj, tmp1, tmp2, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, 4 * BytesPerWord); - __ andi_(R12_tmp2, R12_tmp2, ShenandoahHeap::MARKING); - __ beq(CR0, skip_barrier); - - /* ==== Add previous value directly to thread-local SATB mark queue ==== */ - // Check queue's capacity. Jump to runtime if no free slot is available. - __ ld(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread); - __ cmpdi(CR0, R12_tmp2, 0); - __ beq(CR0, runtime); - - // Capacity suffices. Decrement the queue's size by one slot (size of one oop). - __ addi(R12_tmp2, R12_tmp2, -wordSize); - __ std(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread); - - // Enqueue the previous value and skip the runtime invocation. - __ ld(R11_tmp1, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()), R16_thread); - __ stdx(R0_pre_val, R11_tmp1, R12_tmp2); - __ b(skip_barrier); - - __ bind(runtime); - - /* ==== Invoke runtime to commit SATB mark queue to gc and allocate a new buffer ==== */ - // Save to-be-preserved registers. - const int nbytes_save = (MacroAssembler::num_volatile_regs + caller_stack_slots) * BytesPerWord; - __ save_volatile_gprs(R1_SP, -nbytes_save); - __ save_LR(R11_tmp1); - __ push_frame_reg_args(nbytes_save, R11_tmp1); - - // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), R0_pre_val); - - // Restore to-be-preserved registers. - __ pop_frame(); - __ restore_LR(R11_tmp1); - __ restore_volatile_gprs(R1_SP, -nbytes_save); - - __ bind(skip_barrier); - - // Restore spilled registers. - __ ld(R11_tmp1, -16, R1_SP); - __ ld(R12_tmp2, -24, R1_SP); + // Restore registers + __ ld(tmp2, -32, R1_SP); + __ ld(tmp1, -24, R1_SP); + __ ld(obj, -16, R1_SP); __ blr(); - __ block_comment("} generate_c1_pre_barrier_runtime_stub (shenandoahgc)"); + __ block_comment("} keepalive_barrier_runtime_stub (shenandoahgc)"); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler *sasm, - DecoratorSet decorators) { - __ block_comment("generate_c1_load_reference_barrier_runtime_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { + __ block_comment("load_reference_barrier_runtime_stub (shenandoahgc) {"); - // Argument passing via the stack. - const int caller_stack_slots = 1; + Register obj = R3_ARG1; + Register addr = R4_ARG2; + Register tmp1 = R11_scratch1; + Register tmp2 = R12_scratch2; - // Save to-be-preserved registers. - const int nbytes_save = (MacroAssembler::num_volatile_regs - 1 // 'R3_ARG1' is skipped - + caller_stack_slots) * BytesPerWord; - __ save_volatile_gprs(R1_SP, -nbytes_save, true, false); + // Save registers we are about to clobber + __ std(addr, -24, R1_SP); + __ std(tmp1, -32, R1_SP); + __ std(tmp2, -40, R1_SP); - // Load arguments from stack. - // No load required, as caller has already loaded obj into R3. - Register R3_obj = R3_ARG1; - Register R4_load_addr = R4_ARG2; - __ ld(R4_load_addr, -8, R1_SP); + // Pull the arguments from the stack + __ ld(obj, -8, R1_SP); + __ ld(addr, -16, R1_SP); - Register R11_tmp = R11_scratch1; + load_reference_barrier(sasm, decorators, addr, noreg, obj, tmp1, tmp2, + MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, 5 * BytesPerWord); - /* ==== Invoke runtime ==== */ - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - address jrt_address = nullptr; - - if (is_strong) { - if (is_native) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } else { - if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); - } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } - } - } else if (is_weak) { - assert(!is_native, "weak load reference barrier must not be called off-heap"); - if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); - } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - } - } else { - assert(is_phantom, "reference type must be phantom"); - assert(is_native, "phantom load reference barrier must be called off-heap"); - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); - } - assert(jrt_address != nullptr, "load reference barrier runtime routine cannot be found"); - - __ save_LR(R11_tmp); - __ push_frame_reg_args(nbytes_save, R11_tmp); - - // Invoke runtime. Arguments are already stored in the corresponding registers. - __ call_VM_leaf(jrt_address, R3_obj, R4_load_addr); - - // Restore to-be-preserved registers. - __ pop_frame(); - __ restore_LR(R11_tmp); - __ restore_volatile_gprs(R1_SP, -nbytes_save, true, false); // Skip 'R3_RET' register. + // Restore registers + __ ld(tmp2, -40, R1_SP); + __ ld(tmp1, -32, R1_SP); + __ ld(addr, -24, R1_SP); __ blr(); - __ block_comment("} generate_c1_load_reference_barrier_runtime_stub (shenandoahgc)"); + __ block_comment("} load_reference_barrier_runtime_stub (shenandoahgc)"); } #undef __ diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index bd1043c2d76..0784c8b7148 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -34,7 +34,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; @@ -56,7 +56,8 @@ private: Register base, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); void card_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, @@ -66,7 +67,8 @@ private: Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); /* ==== Helper methods for barrier implementations ==== */ void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, @@ -78,28 +80,26 @@ public: /* ==== C1 stubs ==== */ #ifdef COMPILER1 + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); - + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif /* ==== Available barriers (facades of the actual implementations) ==== */ void satb_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); /* ==== Access api ==== */ virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, @@ -125,6 +125,8 @@ public: virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler *masm, Register obj, const char* msg); + #ifdef COMPILER2 // Entry points from Matcher void load_c2(const MachNode* node, MacroAssembler* masm, Register dst, Register addr, int disp, Register tmp1, Register tmp2, bool narrow, bool acquire); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index b5bfcb0fced..1501934d48f 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -4450,7 +4450,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_ addi(offset, offset, receiver_step); bdnz(L_loop_search_receiver); - // Fast: no receiver, but profile is full + // Fast: no receiver, but profile is not full if (count != noreg) { mtctr(count); } else { diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index ea1768c6afd..d3e08a21640 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2138,6 +2138,9 @@ bool Matcher::match_rule_supported(int opcode) { case Op_CacheWBPreSync: case Op_CacheWBPostSync: return VM_Version::supports_data_cache_line_flush(); + + case Op_OnSpinWait: + return VM_Version::supports_on_spin_wait(); } return true; // Per default match rules are supported. @@ -2348,26 +2351,26 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 28 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI. -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } @@ -3075,13 +3078,6 @@ encode %{ __ bind(done); %} - enc_class enc_cmove_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ - Label done; - __ bso($crx$$CondRegister, done); - __ mffprd($dst$$Register, $src$$FloatRegister); - __ bind(done); - %} - enc_class enc_bc(flagsRegSrc crx, cmpOp cmp, Label lbl) %{ Label d; // dummy __ bind(d); @@ -3553,9 +3549,6 @@ ins_attrib ins_alignment(1); ins_attrib ins_cannot_rematerialize(false); ins_attrib ins_should_rematerialize(false); -// Instruction has variable size depending on alignment. -ins_attrib ins_variable_size_depending_on_alignment(false); - // Instruction is a nop. ins_attrib ins_is_nop(false); @@ -6931,6 +6924,21 @@ instruct membar_CPUOrder() %{ ins_pipe(pipe_class_default); %} +instruct onspinwait() %{ + match(OnSpinWait); + ins_cost(DEFAULT_COST); + + format %{ "OnSpinWait (smt_prio_low ; smt_prio_medium)" %} + size(8); + ins_encode %{ + __ block_comment("spin_wait {"); + __ smt_prio_low(); + __ smt_prio_medium(); + __ block_comment("}"); + %} + ins_pipe(pipe_class_default); +%} + //----------Conditional Move--------------------------------------------------- // Cmove using isel. @@ -6997,8 +7005,6 @@ instruct cmovF_reg(cmpOp cmp, flagsRegSrc crx, regF dst, regF src) %{ match(Set dst (CMoveF (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST+BRANCH_COST); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} size(8); ins_encode %{ @@ -7016,8 +7022,6 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ match(Set dst (CMoveD (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST+BRANCH_COST); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} size(8); ins_encode %{ @@ -8258,8 +8262,6 @@ instruct cmovI_bne_negI_reg(iRegIdst dst, flagsRegSrc crx, iRegIsrc src1) %{ effect(USE_DEF dst, USE src1, USE crx); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVE $dst, neg($src1), $crx" %} size(8); ins_encode %{ @@ -8316,8 +8318,6 @@ instruct cmovL_bne_negL_reg(iRegLdst dst, flagsRegSrc crx, iRegLsrc src1) %{ effect(USE_DEF dst, USE src1, USE crx); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVE $dst, neg($src1), $crx" %} size(8); ins_encode %{ @@ -9148,61 +9148,14 @@ instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -// Left shifted Immediate And -instruct andI_reg_immIhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2, flagsRegCR0 cr0) %{ +instruct andI_reg_immI(iRegIdst dst, iRegIsrc src1, immI src2, flagsRegCR0 cr0) %{ match(Set dst (AndI src1 src2)); + predicate(Assembler::andi_supports((juint)(n->in(2)->get_int()))); effect(KILL cr0); - format %{ "ANDIS $dst, $src1, $src2.hi" %} - size(4); - ins_encode %{ - __ andis_($dst$$Register, $src1$$Register, (int)((unsigned short)(($src2$$constant & 0xFFFF0000) >> 16))); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And -instruct andI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2, flagsRegCR0 cr0) %{ - match(Set dst (AndI src1 src2)); - effect(KILL cr0); - format %{ "ANDI $dst, $src1, $src2" %} size(4); ins_encode %{ - // FIXME: avoid andi_ ? - __ andi_($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And where the immediate is a negative power of 2. -instruct andI_reg_immInegpow2(iRegIdst dst, iRegIsrc src1, immInegpow2 src2) %{ - match(Set dst (AndI src1 src2)); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(juint)$src2$$constant)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andI_reg_immIpow2minus1(iRegIdst dst, iRegIsrc src1, immIpow2minus1 src2) %{ - match(Set dst (AndI src1 src2)); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((juint)$src2$$constant + 1u)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{ - match(Set dst (AndI src1 src2)); - predicate(UseRotateAndMaskInstructionsPPC64); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - int bitpos = 31 - log2i_exact((juint)$src2$$constant); - __ rlwinm($dst$$Register, $src1$$Register, 0, bitpos, bitpos); + __ andi($dst$$Register, $src1$$Register, (juint)$src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} @@ -9220,50 +9173,27 @@ instruct andL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ ins_pipe(pipe_class_default); %} -// Immediate And long -instruct andL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2, flagsRegCR0 cr0) %{ +instruct andL_reg_immL(iRegLdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{ match(Set dst (AndL src1 src2)); + predicate(Assembler::andi_supports(n->in(2)->get_long())); effect(KILL cr0); - format %{ "ANDI $dst, $src1, $src2 \t// long" %} size(4); ins_encode %{ - // FIXME: avoid andi_ ? - __ andi_($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And Long where the immediate is a negative power of 2. -instruct andL_reg_immLnegpow2(iRegLdst dst, iRegLsrc src1, immLnegpow2 src2) %{ - match(Set dst (AndL src1 src2)); - format %{ "ANDDI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(julong)$src2$$constant)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andL_reg_immLpow2minus1(iRegLdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ - match(Set dst (AndL src1 src2)); - format %{ "ANDDI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull)); + __ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} // AndL + ConvL2I. -instruct convL2I_andL_reg_immLpow2minus1(iRegIdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ +instruct convL2I_andL_reg_immL(iRegIdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{ match(Set dst (ConvL2I (AndL src1 src2))); - ins_cost(DEFAULT_COST); - - format %{ "ANDDI $dst, $src1, $src2 \t// long + l2i" %} + predicate(Assembler::andi_supports(n->in(1)->in(2)->get_long())); + effect(KILL cr0); + format %{ "ANDI $dst, $src1, $src2 \t// long + l2i" %} size(4); ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull)); + __ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} @@ -9276,7 +9206,7 @@ instruct orI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ format %{ "OR $dst, $src1, $src2" %} size(4); ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -9288,7 +9218,7 @@ instruct orI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ format %{ "OR $dst, $src1, $src2" %} size(4); ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -9326,7 +9256,7 @@ instruct orL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ size(4); format %{ "OR $dst, $src1, $src2 \t// long" %} ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -9339,7 +9269,7 @@ instruct orI_regL_regL(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ format %{ "OR $dst, $src1, $src2 \t// long + l2i" %} size(4); ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -10008,6 +9938,34 @@ instruct convL2I_reg(iRegIdst dst, iRegLsrc src) %{ ins_pipe(pipe_class_default); %} +instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + format %{ "CMOVI $crx, $dst, $src" %} + size(8); + ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovI_bso_reg_con0(iRegIdst dst, flagsRegSrc crx, regD src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + format %{ "CMOVI $dst, $crx, $src, 0 \t// set to 0 if unordered" %} + size(12); + ins_encode %{ + Label done; + __ li($dst$$Register, 0); + __ bso($crx$$CondRegister, done); + __ mffprd($dst$$Register, $src$$FloatRegister); + __ bind(done); + %} + ins_pipe(pipe_class_default); +%} + instruct convD2IRaw_regD(regD dst, regD src) %{ // no match-rule, false predicate effect(DEF dst, USE src); @@ -10021,88 +9979,6 @@ instruct convD2IRaw_regD(regD dst, regD src) %{ ins_pipe(pipe_class_default); %} -instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ - // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); - predicate(false); - - ins_variable_size_depending_on_alignment(true); - - format %{ "CMOVI $crx, $dst, $src" %} - size(8); - ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); - ins_pipe(pipe_class_default); -%} - -instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ - // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); - predicate(false); - - ins_variable_size_depending_on_alignment(true); - - format %{ "CMOVI $crx, $dst, $src" %} - size(8); - ins_encode( enc_cmove_bso_reg(dst, crx, src) ); - ins_pipe(pipe_class_default); -%} - - -instruct cmovI_bso_reg_conLvalue0_Ex(iRegIdst dst, flagsRegSrc crx, regD src) %{ - // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); - predicate(false); - - format %{ "CMOVI $dst, $crx, $src \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region dst crx src - // \ | | / - // dst=cmovI_bso_reg_conLvalue0 - // - // with - // - // region dst - // \ / - // dst=loadConI16(0) - // | - // ^ region dst crx src - // | \ | | / - // dst=cmovI_bso_reg - // - - // Create new nodes. - MachNode *m1 = new loadConI16Node(); - MachNode *m2 = new cmovI_bso_regNode(); - - // inputs for new nodes - m1->add_req(n_region); - m2->add_req(n_region, n_crx, n_src); - - // precedences for new nodes - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_dst; - m1->_opnds[1] = new immI16Oper(0); - - m2->_opnds[0] = op_dst; - m2->_opnds[1] = op_crx; - m2->_opnds[2] = op_src; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); - %} -%} - - // Double to Int conversion, NaN is mapped to 0. Special version for Power8. instruct convD2I_reg_mffprd_ExEx(iRegIdst dst, regD src) %{ match(Set dst (ConvD2I src)); @@ -10113,7 +9989,7 @@ instruct convD2I_reg_mffprd_ExEx(iRegIdst dst, regD src) %{ flagsReg crx; cmpDUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convD2IRaw_regD(tmpD, src); // Convert float to int (speculated). - cmovI_bso_reg_conLvalue0_Ex(dst, crx, tmpD); // Cmove based on NaN check. + cmovI_bso_reg_con0(dst, crx, tmpD); // Cmove based on NaN check. %} %} @@ -10141,7 +10017,7 @@ instruct convF2I_regF_mffprd_ExEx(iRegIdst dst, regF src) %{ flagsReg crx; cmpFUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convF2IRaw_regF(tmpF, src); // Convert float to int (speculated). - cmovI_bso_reg_conLvalue0_Ex(dst, crx, tmpF); // Cmove based on NaN check. + cmovI_bso_reg_con0(dst, crx, tmpF); // Cmove based on NaN check. %} %} @@ -10183,6 +10059,34 @@ instruct zeroExtendL_regL(iRegLdst dst, iRegLsrc src, immL_32bits mask) %{ ins_pipe(pipe_class_default); %} +instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + format %{ "CMOVL $crx, $dst, $src" %} + size(8); + ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovL_bso_reg_con0(iRegLdst dst, flagsRegSrc crx, regD src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + format %{ "CMOVL $dst, $crx, $src, 0 \t// set to 0 if unordered" %} + size(12); + ins_encode %{ + Label done; + __ li($dst$$Register, 0); + __ bso($crx$$CondRegister, done); + __ mffprd($dst$$Register, $src$$FloatRegister); + __ bind(done); + %} + ins_pipe(pipe_class_default); +%} + instruct convF2LRaw_regF(regF dst, regF src) %{ // no match-rule, false predicate effect(DEF dst, USE src); @@ -10196,85 +10100,6 @@ instruct convF2LRaw_regF(regF dst, regF src) %{ ins_pipe(pipe_class_default); %} -instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ - // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); - predicate(false); - - ins_variable_size_depending_on_alignment(true); - - format %{ "CMOVL $crx, $dst, $src" %} - size(8); - ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); - ins_pipe(pipe_class_default); -%} - -instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ - // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); - predicate(false); - - ins_variable_size_depending_on_alignment(true); - - format %{ "CMOVL $crx, $dst, $src" %} - size(8); - ins_encode( enc_cmove_bso_reg(dst, crx, src) ); - ins_pipe(pipe_class_default); -%} - - -instruct cmovL_bso_reg_conLvalue0_Ex(iRegLdst dst, flagsRegSrc crx, regD src) %{ - // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); - predicate(false); - - format %{ "CMOVL $dst, $crx, $src \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region dst crx src - // \ | | / - // dst=cmovL_bso_reg_conLvalue0 - // - // with - // - // region dst - // \ / - // dst=loadConL16(0) - // | - // ^ region dst crx src - // | \ | | / - // dst=cmovL_bso_reg - // - - // Create new nodes. - MachNode *m1 = new loadConL16Node(); - MachNode *m2 = new cmovL_bso_regNode(); - - // inputs for new nodes - m1->add_req(n_region); - m2->add_req(n_region, n_crx, n_src); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_dst; - m1->_opnds[1] = new immL16Oper(0); - m2->_opnds[0] = op_dst; - m2->_opnds[1] = op_crx; - m2->_opnds[2] = op_src; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); - %} -%} - - // Float to Long conversion, NaN is mapped to 0. Special version for Power8. instruct convF2L_reg_mffprd_ExEx(iRegLdst dst, regF src) %{ match(Set dst (ConvF2L src)); @@ -10285,7 +10110,7 @@ instruct convF2L_reg_mffprd_ExEx(iRegLdst dst, regF src) %{ flagsReg crx; cmpFUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convF2LRaw_regF(tmpF, src); // Convert float to long (speculated). - cmovL_bso_reg_conLvalue0_Ex(dst, crx, tmpF); // Cmove based on NaN check. + cmovL_bso_reg_con0(dst, crx, tmpF); // Cmove based on NaN check. %} %} @@ -10302,7 +10127,6 @@ instruct convD2LRaw_regD(regD dst, regD src) %{ ins_pipe(pipe_class_default); %} - // Double to Long conversion, NaN is mapped to 0. Special version for Power8. instruct convD2L_reg_mffprd_ExEx(iRegLdst dst, regD src) %{ match(Set dst (ConvD2L src)); @@ -10313,7 +10137,7 @@ instruct convD2L_reg_mffprd_ExEx(iRegLdst dst, regD src) %{ flagsReg crx; cmpDUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convD2LRaw_regD(tmpD, src); // Convert float to long (speculated). - cmovL_bso_reg_conLvalue0_Ex(dst, crx, tmpD); // Cmove based on NaN check. + cmovL_bso_reg_con0(dst, crx, tmpD); // Cmove based on NaN check. %} %} @@ -10835,84 +10659,22 @@ instruct cmpFUnordered_reg_reg(flagsReg crx, regF src1, regF src2) %{ ins_pipe(pipe_class_default); %} -instruct cmov_bns_less(flagsReg crx) %{ - // no match-rule, false predicate - effect(DEF crx); - predicate(false); - - ins_variable_size_depending_on_alignment(true); - - format %{ "CMOV $crx" %} - size(12); - ins_encode %{ - Label done; - __ bns($crx$$CondRegister, done); // not unordered -> keep crx - __ li(R0, 0); - __ cmpwi($crx$$CondRegister, R0, 1); // unordered -> set crx to 'less' - __ bind(done); - %} - ins_pipe(pipe_class_default); -%} - // Compare floating, generate condition code. -instruct cmpF_reg_reg_Ex(flagsReg crx, regF src1, regF src2) %{ - // FIXME: should we match 'If cmp (CmpF src1 src2))' ?? - // - // The following code sequence occurs a lot in mpegaudio: - // - // block BXX: - // 0: instruct cmpFUnordered_reg_reg (cmpF_reg_reg-0): - // cmpFUrd CR6, F11, F9 - // 4: instruct cmov_bns_less (cmpF_reg_reg-1): - // cmov CR6 - // 8: instruct branchConSched: - // B_FARle CR6, B56 P=0.500000 C=-1.000000 +instruct cmpF_reg_reg(flagsReg crx, regF src1, regF src2) %{ match(Set crx (CmpF src1 src2)); ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CMPF $crx, $src1, $src2 \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region src1 src2 - // \ | | - // crx=cmpF_reg_reg - // - // with - // - // region src1 src2 - // \ | | - // crx=cmpFUnordered_reg_reg - // | - // ^ region - // | \ - // crx=cmov_bns_less - // - - // Create new nodes. - MachNode *m1 = new cmpFUnordered_reg_regNode(); - MachNode *m2 = new cmov_bns_lessNode(); - - // inputs for new nodes - m1->add_req(n_region, n_src1, n_src2); - m2->add_req(n_region); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_crx; - m1->_opnds[1] = op_src1; - m1->_opnds[2] = op_src2; - m2->_opnds[0] = op_crx; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); + format %{ "CMPF $crx, $src1, $src2" %} + size(16); + ins_encode %{ + Label done; + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + __ bns($crx$$CondRegister, done); + __ li(R0, 0); + __ cmpwi($crx$$CondRegister, R0, 1); + __ bind(done); %} + ins_pipe(pipe_class_default); %} // Compare float, generate -1,0,1 @@ -10950,53 +10712,21 @@ instruct cmpDUnordered_reg_reg(flagsReg crx, regD src1, regD src2) %{ ins_pipe(pipe_class_default); %} -instruct cmpD_reg_reg_Ex(flagsReg crx, regD src1, regD src2) %{ +instruct cmpD_reg_reg(flagsReg crx, regD src1, regD src2) %{ match(Set crx (CmpD src1 src2)); ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CmpD $crx, $src1, $src2 \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region src1 src2 - // \ | | - // crx=cmpD_reg_reg - // - // with - // - // region src1 src2 - // \ | | - // crx=cmpDUnordered_reg_reg - // | - // ^ region - // | \ - // crx=cmov_bns_less - // - - // create new nodes - MachNode *m1 = new cmpDUnordered_reg_regNode(); - MachNode *m2 = new cmov_bns_lessNode(); - - // inputs for new nodes - m1->add_req(n_region, n_src1, n_src2); - m2->add_req(n_region); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_crx; - m1->_opnds[1] = op_src1; - m1->_opnds[2] = op_src2; - m2->_opnds[0] = op_crx; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); + format %{ "CMPD $crx, $src1, $src2" %} + size(16); + ins_encode %{ + Label done; + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + __ bns($crx$$CondRegister, done); + __ li(R0, 0); + __ cmpwi($crx$$CondRegister, R0, 1); + __ bind(done); %} + ins_pipe(pipe_class_default); %} // Compare double, generate -1,0,1 diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index fc444cb6923..a1446f49797 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -2320,10 +2320,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Make sure that thread is non-volatile; it crosses a bunch of VM calls below. assert(R16_thread->is_nonvolatile(), "thread must be in non-volatile register"); -# if 0 - // DTrace method entry -# endif - // Lock a synchronized method. // -------------------------------------------------------------------------- @@ -2595,10 +2591,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ bind(done); } -# if 0 - // DTrace method exit -# endif - // Clear "last Java frame" SP and PC. // -------------------------------------------------------------------------- diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.hpp b/src/hotspot/cpu/ppc/vm_version_ppc.hpp index 0f4eb3593a3..4f3b6d37911 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.hpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.hpp @@ -67,6 +67,8 @@ public: static bool supports_float16() { return PowerArchitecturePPC64 >= 9; } + static bool supports_on_spin_wait() { return true; } + static bool is_determine_features_test_running() { return _is_determine_features_test_running; } // CPU instruction support static bool has_mfdscr() { return (_features & mfdscr_m) != 0; } // Power8, but may be unavailable (QEMU) diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index ce5a8dde230..5cf341aa21b 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_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) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -103,8 +103,6 @@ public: enum { - pc_return_offset = 0, - // All frames link_offset = -2, return_addr_offset = -1, @@ -220,8 +218,6 @@ // deoptimization support void interpreter_frame_set_last_sp(intptr_t* last_sp); - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index fd78b429ee4..f16b22e5575 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -352,8 +352,8 @@ void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register __ bne(tmp1, tmp2, error); // Make sure klass is 'reasonable', which is not zero. - __ load_klass(obj, obj, tmp1); // get klass - __ beqz(obj, error); // if klass is null it is broken + __ load_narrow_klass(tmp1, obj); // get klass + __ beqz(tmp1, error); // if klass is null it is broken } void BarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index eec5f9a5165..d7cfcb11205 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -433,6 +433,32 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ bind(done); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) { + // Check if the oop is in the right area of memory + __ mv(tmp2, (intptr_t) Universe::verify_oop_mask()); + __ andr(tmp1, obj, tmp2); + __ mv(tmp2, (intptr_t) Universe::verify_oop_bits()); + + // Compare tmp1 and tmp2. + __ bne(tmp1, tmp2, L_error); + + // This routine is sometimes called before applying GC barriers. + // With +COH, loading the klass may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + Address gc_state(xthread, ShenandoahThreadLocalData::gc_state_offset()); + __ lbu(tmp1, gc_state); + __ test_bit(tmp1, tmp1, ShenandoahHeap::HAS_FORWARDED_BITPOS); + __ bnez(tmp1, L_skip); + } + + // Make sure klass is 'reasonable', which is not zero. + __ load_narrow_klass(tmp1, obj); + __ beqz(tmp1, L_error); + + __ bind(L_skip); +} + void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register start, Register count, Register tmp) { assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); @@ -471,74 +497,39 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - Register pre_val_reg = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /* wide */); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, false /* wide */); } - __ beqz(pre_val_reg, *stub->continuation(), /* is_far */ true); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); + __ beqz(obj, *stub->continuation(), /* is_far */ true); + + ce->store_parameter(obj, 0); + __ far_call(RuntimeAddress(bs->keepalive_barrier_stub())); __ j(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, - ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == x10, "C1 must know about our slow call result register"); - assert(res == x10, "result must arrive in x10"); - assert_different_registers(tmp1, tmp2, t0); - - if (res != obj) { - __ mv(res, obj); - } - - if (is_strong) { - // Check for object in cset. - __ mv(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ srli(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ add(tmp2, tmp2, tmp1); - __ lbu(tmp2, Address(tmp2)); - __ beqz(tmp2, *stub->continuation(), true /* is_far */); - } - - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - - if (is_strong) { - if (is_native) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ far_call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mv(obj, slow_result); } __ j(*stub->continuation()); @@ -548,92 +539,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - - // arg0 : previous value of memory - - BarrierSet* bs = BarrierSet::barrier_set(); - - const Register pre_val = x10; - const Register thread = xthread; - const Register tmp = t0; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is marking still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ lb(tmp, gc_state); - __ test_bit(tmp, tmp, ShenandoahHeap::MARKING_BITPOS); - __ beqz(tmp, done); - - // Can we store original value in the thread's buffer? - __ ld(tmp, queue_index); - __ beqz(tmp, runtime); - - __ subi(tmp, tmp, wordSize); - __ sd(tmp, queue_index); - __ ld(t1, buffer); - __ add(tmp, tmp, t1); - __ load_parameter(0, t1); - __ sd(t1, Address(tmp, 0)); - __ j(done); - - __ bind(runtime); - __ push_call_clobbered_registers(); - __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - __ pop_call_clobbered_registers(); - __ bind(done); - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = x10; + const Register tmp1 = x11; + const Register tmp2 = x12; + __ push_reg(RegSet::of(tmp1, tmp2, tmp_obj), sp); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, xthread, tmp1, tmp2); + __ pop_reg(RegSet::of(tmp1, tmp2, tmp_obj), sp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, - DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ push_call_clobbered_registers(); - __ load_parameter(0, x10); - __ load_parameter(1, x11); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - address target = nullptr; - if (is_strong) { - if (is_native) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } else { - if (UseCompressedOops) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); - } else { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); - } else { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); - } - __ rt_call(target); - __ mv(t0, x10); - __ pop_call_clobbered_registers(); - __ mv(x10, t0); - + const Register tmp_obj = x10; + const Register tmp_addr = x11; + __ push_reg(RegSet::of(tmp_addr), sp); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop_reg(RegSet::of(tmp_addr), sp); __ epilogue(); } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index d41809f1ef7..eb8ac653e2e 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -33,7 +33,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -79,12 +79,14 @@ public: Register obj, Register tmp, Label& slowpath); virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index bf37ccf64e2..2f8491dd592 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -1039,7 +1039,7 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe __ bind(check_oop); // Make sure klass is 'reasonable', which is not zero - __ load_klass(tmp1, obj, tmp2); + __ load_narrow_klass(tmp1, obj); __ beqz(tmp1, error); __ bind(check_zaddress); diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index f05b9ff7791..a7f0da42f4e 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -103,7 +103,7 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseZbb, false, DIAGNOSTIC, "Use Zbb instructions") \ product(bool, UseZbkb, false, EXPERIMENTAL, "Use Zbkb instructions") \ product(bool, UseZbs, false, DIAGNOSTIC, "Use Zbs instructions") \ - product(bool, UseZfa, false, EXPERIMENTAL, "Use Zfa instructions") \ + product(bool, UseZfa, false, DIAGNOSTIC, "Use Zfa instructions") \ product(bool, UseZfh, false, DIAGNOSTIC, "Use Zfh instructions") \ product(bool, UseZfhmin, false, DIAGNOSTIC, "Use Zfhmin instructions") \ product(bool, UseZacas, false, EXPERIMENTAL, "Use Zacas instructions") \ @@ -117,9 +117,10 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseZihintpause, false, EXPERIMENTAL, \ "Use Zihintpause instructions") \ product(bool, UseZtso, false, EXPERIMENTAL, "Assume Ztso memory model") \ - product(bool, UseZvbb, false, EXPERIMENTAL, "Use Zvbb instructions") \ + product(bool, UseZvbb, false, DIAGNOSTIC, "Use Zvbb instructions") \ product(bool, UseZvbc, false, EXPERIMENTAL, "Use Zvbc instructions") \ product(bool, UseZvfh, false, DIAGNOSTIC, "Use Zvfh instructions") \ + product(bool, UseZvfhmin, false, DIAGNOSTIC, "Use Zvfhmin instructions") \ product(bool, UseZvkg, false, DIAGNOSTIC, "Use Zvkg instructions") \ product(bool, UseZvkn, false, DIAGNOSTIC, \ "Use Zvkn group extension, Zvkned, Zvknhb, Zvkb, Zvkt") \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index d93329544a7..7a339d83d25 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -3767,16 +3767,19 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { srli(dst, dst, markWord::klass_shift); } +void MacroAssembler::load_narrow_klass(Register dst, Register src) { + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(dst, src); + } else { + lwu(dst, Address(src, oopDesc::klass_offset_in_bytes())); + } +} + void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { assert_different_registers(dst, tmp); assert_different_registers(src, tmp); - if (UseCompactObjectHeaders) { - load_narrow_klass_compact(dst, src); - decode_klass_not_null(dst, tmp); - } else { - lwu(dst, Address(src, oopDesc::klass_offset_in_bytes())); - decode_klass_not_null(dst, tmp); - } + load_narrow_klass(dst, src); + decode_klass_not_null(dst, tmp); } void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index a5ad7eeaa5f..f28e828fb65 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -197,6 +197,7 @@ class MacroAssembler: public Assembler { Register val, Register tmp1, Register tmp2, Register tmp3); void load_klass(Register dst, Register src, Register tmp = t0); void load_narrow_klass_compact(Register dst, Register src); + void load_narrow_klass(Register dst, Register src); void store_klass(Register dst, Register src, Register tmp = t0); void cmp_klass_beq(Register obj, Register klass, Register tmp1, Register tmp2, diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 0c077dc84a3..e022dcb4262 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1105,8 +1105,9 @@ void reg_mask_init() { _NO_SPECIAL_PTR_REG_mask.assignFrom(_ALL_REG_mask); _NO_SPECIAL_PTR_REG_mask.subtract(_NON_ALLOCATABLE_REG_mask); - // x27 is not allocatable when compressed oops is on - if (UseCompressedOops) { + // x27 is not allocatable when compressed oops is on and heapbase is not zero, + // compressed klass pointers doesn't use x27 when heapbase is zero. + if (UseCompressedOops && (CompressedOops::base() != nullptr)) { _NO_SPECIAL_REG32_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); _NO_SPECIAL_REG_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); _NO_SPECIAL_PTR_REG_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); @@ -2100,25 +2101,25 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -const RegMask& Matcher::divI_proj_mask() { +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index a0af43364cb..2a63221de04 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -113,6 +113,7 @@ source %{ break; case Op_VectorCastHF2F: case Op_VectorCastF2HF: + return UseZvfh || UseZvfhmin; case Op_AddVHF: case Op_SubVHF: case Op_MulVHF: diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 13f3ed4de89..06cf67e2486 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -1889,7 +1889,7 @@ class StubGenerator: public StubCodeGenerator { __ sext(scratch_length, length, 32); // length (elements count, 32-bits value) __ bltz(scratch_length, L_failed); - __ load_klass(scratch_src_klass, src); + __ load_narrow_klass(scratch_src_klass, src); #ifdef ASSERT { BLOCK_COMMENT("assert klasses not null {"); @@ -1898,11 +1898,12 @@ class StubGenerator: public StubCodeGenerator { __ bind(L1); __ stop("broken null klass"); __ bind(L2); - __ load_klass(t0, dst, t1); + __ load_narrow_klass(t0, dst); __ beqz(t0, L1); // this would be broken also BLOCK_COMMENT("} assert klasses not null done"); } #endif + __ decode_klass_not_null(scratch_src_klass, t0); // Load layout helper (32-bits) // diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 11a88dfedd7..e5d925d1bea 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -219,78 +219,80 @@ class VM_Version : public Abstract_VM_Version { // // Fields description in `decl`: // declaration name, extension name, bit value from linux, feature string?, mapped flag) - #define RV_EXT_FEATURE_FLAGS(decl) \ - /* A Atomic Instructions */ \ - decl(a , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* C Compressed Instructions */ \ - decl(c , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \ - /* D Single-Precision Floating-Point */ \ - decl(d , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* F Single-Precision Floating-Point */ \ - decl(f , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* H Hypervisor */ \ - decl(h , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* I RV64I */ \ - decl(i , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* M Integer Multiplication and Division */ \ - decl(m , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* Q Quad-Precision Floating-Point */ \ - decl(q , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* V Vector */ \ - decl(v , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \ - \ - /* ----------------------- Other extensions ----------------------- */ \ - \ - /* Atomic compare-and-swap (CAS) instructions */ \ - decl(Zacas , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ - /* Zba Address generation instructions */ \ - decl(Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ - /* Zbb Basic bit-manipulation */ \ - decl(Zbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \ - /* Zbc Carry-less multiplication */ \ - decl(Zbc , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Bitmanip instructions for Cryptography */ \ - decl(Zbkb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \ - /* Zbs Single-bit instructions */ \ - decl(Zbs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \ - /* Zcb Simple code-size saving instructions */ \ - decl(Zcb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \ - /* Additional Floating-Point instructions */ \ - decl(Zfa , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \ - /* Zfh Half-Precision Floating-Point instructions */ \ - decl(Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ - /* Zfhmin Minimal Half-Precision Floating-Point instructions */ \ - decl(Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ - /* Zicbom Cache Block Management Operations */ \ - decl(Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ - /* Zicbop Cache Block Prefetch Operations */ \ - decl(Zicbop , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \ - /* Zicboz Cache Block Zero Operations */ \ - decl(Zicboz , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \ - /* Base Counters and Timers */ \ - decl(Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Zicond Conditional operations */ \ - decl(Zicond , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ - /* Zicsr Control and Status Register (CSR) Instructions */ \ - decl(Zicsr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. */ \ - decl(Zic64b , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \ - /* Zifencei Instruction-Fetch Fence */ \ - decl(Zifencei , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Zihintpause Pause instruction HINT */ \ - decl(Zihintpause , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \ - /* Total Store Ordering */ \ - decl(Ztso , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \ - /* Vector Basic Bit-manipulation */ \ - decl(Zvbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, &ext_v, nullptr)) \ - /* Vector Carryless Multiplication */ \ - decl(Zvbc , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, &ext_v, nullptr)) \ - /* Vector Extension for Half-Precision Floating-Point */ \ - decl(Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_v, &ext_Zfh, nullptr)) \ - /* Shorthand for Zvkned + Zvknhb + Zvkb + Zvkt */ \ - decl(Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_v, nullptr)) \ - /* Zvkg crypto extension for ghash and gcm */ \ - decl(Zvkg , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkg, &ext_v, nullptr)) \ + #define RV_EXT_FEATURE_FLAGS(decl) \ + /* A Atomic Instructions */ \ + decl(a , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* C Compressed Instructions */ \ + decl(c , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \ + /* D Single-Precision Floating-Point */ \ + decl(d , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* F Single-Precision Floating-Point */ \ + decl(f , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* H Hypervisor */ \ + decl(h , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* I RV64I */ \ + decl(i , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* M Integer Multiplication and Division */ \ + decl(m , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* Q Quad-Precision Floating-Point */ \ + decl(q , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* V Vector */ \ + decl(v , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \ + \ + /* ----------------------- Other extensions ----------------------- */ \ + \ + /* Atomic compare-and-swap (CAS) instructions */ \ + decl(Zacas , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ + /* Zba Address generation instructions */ \ + decl(Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ + /* Zbb Basic bit-manipulation */ \ + decl(Zbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \ + /* Zbc Carry-less multiplication */ \ + decl(Zbc , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Bitmanip instructions for Cryptography */ \ + decl(Zbkb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \ + /* Zbs Single-bit instructions */ \ + decl(Zbs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \ + /* Zcb Simple code-size saving instructions */ \ + decl(Zcb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \ + /* Additional Floating-Point instructions */ \ + decl(Zfa , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \ + /* Zfh Half-Precision Floating-Point instructions */ \ + decl(Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ + /* Zfhmin Minimal Half-Precision Floating-Point instructions */ \ + decl(Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ + /* Zicbom Cache Block Management Operations */ \ + decl(Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ + /* Zicbop Cache Block Prefetch Operations */ \ + decl(Zicbop , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \ + /* Zicboz Cache Block Zero Operations */ \ + decl(Zicboz , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \ + /* Base Counters and Timers */ \ + decl(Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zicond Conditional operations */ \ + decl(Zicond , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ + /* Zicsr Control and Status Register (CSR) Instructions */ \ + decl(Zicsr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. */ \ + decl(Zic64b , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \ + /* Zifencei Instruction-Fetch Fence */ \ + decl(Zifencei , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zihintpause Pause instruction HINT */ \ + decl(Zihintpause , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \ + /* Total Store Ordering */ \ + decl(Ztso , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \ + /* Vector Basic Bit-manipulation */ \ + decl(Zvbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, &ext_v, nullptr)) \ + /* Vector Carryless Multiplication */ \ + decl(Zvbc , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, &ext_v, nullptr)) \ + /* Vector Extension for Half-Precision Floating-Point */ \ + decl(Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_v, &ext_Zfhmin, nullptr)) \ + /* Vector Extension for Minimal Half-Precision Floating-Point */ \ + decl(Zvfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfhmin, &ext_v, nullptr)) \ + /* Shorthand for Zvkned + Zvknhb + Zvkb + Zvkt */ \ + decl(Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_v, nullptr)) \ + /* Zvkg crypto extension for ghash and gcm */ \ + decl(Zvkg , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkg, &ext_v, nullptr)) \ #define DECLARE_RV_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ struct ext_##PRETTY##RVExtFeatureValue : public RVExtFeatureValue { \ @@ -442,6 +444,7 @@ private: RV_ENABLE_EXTENSION(UseZicboz) \ RV_ENABLE_EXTENSION(UseZicond) \ RV_ENABLE_EXTENSION(UseZihintpause) \ + RV_ENABLE_EXTENSION(UseZvfhmin) \ static void useRVA23U64Profile(); diff --git a/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp b/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp index 96990f0ce94..c54f1a4b010 100644 --- a/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp +++ b/src/hotspot/cpu/s390/abstractInterpreter_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 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -197,8 +197,10 @@ void AbstractInterpreter::layout_activation(Method* method, assert(is_bottom_frame && (sender_sp == caller->unextended_sp()), "must initialize sender_sp of bottom skeleton frame when pushing it"); } else { - assert(caller->is_entry_frame() || caller->is_upcall_stub_frame(), "is there a new frame type??"); - sender_sp = caller->sp(); // Call_stub only uses it's fp. + // For entry, upcall_stub, and native frames, sender_sp is simply the caller's sp. + // These frames use the standard C ABI and don't require adjustment. + assert(caller->is_entry_frame() || caller->is_upcall_stub_frame() || caller->is_native_frame(), "is there a new frame type??"); + sender_sp = caller->sp(); } interpreter_frame->interpreter_frame_set_method(method); diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp index c0cee5bd555..c834a71ec0c 100644 --- a/src/hotspot/cpu/s390/assembler_s390.hpp +++ b/src/hotspot/cpu/s390/assembler_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. * @@ -3279,6 +3279,9 @@ class Assembler : public AbstractAssembler { static bool is_z_nop(address x) { return is_z_nop(* (short *) x); } + static bool is_z_illtrap(address x) { + return *(uint8_t*)x == 0u; + } static bool is_z_br(long x) { return is_z_bcr(x) && ((x & 0x00f0) == 0x00f0); } diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 38698370faa..db3f2f6218f 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -524,6 +524,7 @@ void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) { __ z_nop(); __ z_brasl(Z_R14, op->addr()); add_call_info(code_offset(), op->info()); + __ post_call_nop(); } void LIR_Assembler::ic_call(LIR_OpJavaCall* op) { @@ -539,7 +540,7 @@ void LIR_Assembler::ic_call(LIR_OpJavaCall* op) { // CALL to fixup routine. Fixup routine uses ScopeDesc info // to determine who we intended to call. __ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr)); - call(op, relocInfo::none); + call(op, relocInfo::none); // call will emit a post call nop, see above method. } void LIR_Assembler::move_regs(Register from_reg, Register to_reg) { @@ -2413,32 +2414,9 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { } void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, - Register recv, Register tmp1, Label* update_done) { - uint i; - for (i = 0; i < VirtualCallData::row_limit(); i++) { - Label next_test; - // See if the receiver is receiver[n]. - Address receiver_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i))); - __ z_cg(recv, receiver_addr); - __ z_brne(next_test); - Address data_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i))); - __ add2mem_64(data_addr, DataLayout::counter_increment, tmp1); - __ branch_optimized(Assembler::bcondAlways, *update_done); - __ bind(next_test); - } - - // Didn't find receiver; find next empty slot and fill it in. - for (i = 0; i < VirtualCallData::row_limit(); i++) { - Label next_test; - Address recv_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i))); - __ z_ltg(Z_R0_scratch, recv_addr); - __ z_brne(next_test); - __ z_stg(recv, recv_addr); - __ load_const_optimized(tmp1, DataLayout::counter_increment); - __ z_stg(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)), mdo); - __ branch_optimized(Assembler::bcondAlways, *update_done); - __ bind(next_test); - } + Register recv, Register tmp1) { + int mdp_offset = md->byte_offset_of_slot(data, in_ByteSize(0)); + __ profile_receiver_type(recv, mdo, mdp_offset, tmp1); } void LIR_Assembler::setup_md_access(ciMethod* method, int bci, @@ -2510,13 +2488,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L __ branch_optimized(Assembler::bcondAlways, *obj_is_null); __ bind(not_null); - NearLabel update_done; Register recv = k_RInfo; __ load_klass(recv, obj); - type_profile_helper(mdo, md, data, recv, Rtmp1, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ add2mem_64(counter_addr, DataLayout::counter_increment, Rtmp1); - __ bind(update_done); + type_profile_helper(mdo, md, data, recv, Rtmp1); } else { __ compareU64_and_branch(obj, (intptr_t) 0, Assembler::bcondEqual, *obj_is_null); } @@ -2606,13 +2580,9 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { __ branch_optimized(Assembler::bcondAlways, done); __ bind(not_null); - NearLabel update_done; Register recv = k_RInfo; __ load_klass(recv, value); - type_profile_helper(mdo, md, data, recv, Rtmp1, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ add2mem_64(counter_addr, DataLayout::counter_increment, Rtmp1); - __ bind(update_done); + type_profile_helper(mdo, md, data, recv, Rtmp1); } else { __ compareU64_and_branch(value, (intptr_t) 0, Assembler::bcondEqual, done); } @@ -2772,11 +2742,8 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { // statically update the MethodData* rather than needing to do // dynamic tests on the receiver type. - // NOTE: we should probably put a lock around this search to - // avoid collisions by concurrent compilations. ciVirtualCallData* vc_data = (ciVirtualCallData*) data; - uint i; - for (i = 0; i < VirtualCallData::row_limit(); i++) { + for (uint i = 0; i < VirtualCallData::row_limit(); i++) { ciKlass* receiver = vc_data->receiver(i); if (known_klass->equals(receiver)) { Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); @@ -2784,32 +2751,13 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { return; } } - - // Receiver type not found in profile data. Select an empty slot. - - // Note that this is less efficient than it should be because it - // always does a write to the receiver part of the - // VirtualCallData rather than just the first time. - for (i = 0; i < VirtualCallData::row_limit(); i++) { - ciKlass* receiver = vc_data->receiver(i); - if (receiver == nullptr) { - Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i))); - metadata2reg(known_klass->constant_encoding(), tmp1); - __ z_stg(tmp1, recv_addr); - Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); - __ add2mem_64(data_addr, DataLayout::counter_increment, tmp1); - return; - } - } + // Receiver type is not found in profile data. + // Fall back to runtime helper to handle the rest at runtime. + metadata2reg(known_klass->constant_encoding(), recv); } else { __ load_klass(recv, recv); - NearLabel update_done; - type_profile_helper(mdo, md, data, recv, tmp1, &update_done); - // Receiver did not match any saved receiver and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - __ add2mem_64(counter_addr, DataLayout::counter_increment, tmp1); - __ bind(update_done); } + type_profile_helper(mdo, md, data, recv, tmp1); } else { // static call __ add2mem_64(counter_addr, DataLayout::counter_increment, tmp1); @@ -2845,6 +2793,7 @@ void LIR_Assembler::rt_call(LIR_Opr result, address dest, if (info != nullptr) { add_call_info_here(info); } + __ post_call_nop(); } void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.hpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.hpp index 9fcf7c0c221..73d25f77d05 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.hpp @@ -30,7 +30,7 @@ // Record the type of the receiver in ReceiverTypeData. void type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, - Register recv, Register tmp1, Label* update_done); + Register recv, Register tmp1); // Setup pointers to MDO, MDO slot, also compute offset bias to access the slot. void setup_md_access(ciMethod* method, int bci, ciMethodData*& md, ciProfileData*& data, int& mdo_offset_bias); diff --git a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp index e78b04fe911..d26db67d078 100644 --- a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp +++ b/src/hotspot/cpu/s390/c1_Runtime1_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, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -52,13 +52,8 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre set_num_rt_args(0); // Nothing on stack. assert(!(oop_result1->is_valid() || metadata_result->is_valid()) || oop_result1 != metadata_result, "registers must be different"); - // We cannot trust that code generated by the C++ compiler saves R14 - // to z_abi_160.return_pc, because sometimes it spills R14 using stmg at - // z_abi_160.gpr14 (e.g. InterpreterRuntime::_new()). - // Therefore we load the PC into Z_R1_scratch and let set_last_Java_frame() save - // it into the frame anchor. - address pc = get_PC(Z_R1_scratch); - int call_offset = (int)(pc - addr_at(0)); + Label resume; + z_larl(Z_R1_scratch, resume); set_last_Java_frame(Z_SP, Z_R1_scratch); // ARG1 must hold thread address. @@ -67,9 +62,12 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre address return_pc = nullptr; align_call_far_patchable(this->pc()); return_pc = call_c_opt(entry_point); + + bind(resume); + int call_offset = offset(); assert(return_pc != nullptr, "const section overflow"); - reset_last_Java_frame(); + reset_last_Java_frame(/* check_last_java_sp= */ false); // Check for pending exceptions. { @@ -208,8 +206,37 @@ void Runtime1::initialize_pd() { } uint Runtime1::runtime_blob_current_thread_offset(frame f) { - Unimplemented(); - return 0; + CodeBlob* cb = f.cb(); + assert(cb == Runtime1::blob_for(StubId::c1_monitorenter_id) || + cb == Runtime1::blob_for(StubId::c1_monitorenter_nofpu_id), "must be"); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + // Calculate the offset of Z_thread (Z_R8) in the saved register area. + // Both c1_monitorenter_id and c1_monitorenter_nofpu_id have the same frame layout: + // - c1_monitorenter_id uses RegisterSaver::all_registers (saves FPU regs) + // - c1_monitorenter_nofpu_id uses RegisterSaver::all_integer_registers (excludes FPU regs but reserves space) + // + // From RegisterSaver_LiveRegs and RegisterSaver_LiveIntRegs: + // Both have 15 float register slots (F0, F2-F15, F1 is excluded as scratch) + // Then integer registers: R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13 + // Z_thread is Z_R8, which is the 7th integer register (index 6 from R2) + // + // Stack layout from SP: + // [0..159] : z_abi_160 + // [160..279] : 15 float register slots (15 * 8 = 120 bytes) + // [280..327] : R2-R7 (6 * 8 = 48 bytes) + // [328..335] : R8 (Z_thread) <- this is what we need + // + // Offset = 160 + 120 + 48 = 328 bytes from SP + // Return value is in 64-bit words: 328 / 8 = 41 + + const int float_reg_slots = 15; // F0, F2-F15 (F1 is scratch, excluded) + const int int_regs_before_r8 = 6; // R2, R3, R4, R5, R6, R7 + const int z_thread_offset = frame::z_abi_160_size + + (float_reg_slots * 8) + + (int_regs_before_r8 * 8); + + return z_thread_offset / wordSize; } OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { diff --git a/src/hotspot/cpu/s390/continuationEntry_s390.hpp b/src/hotspot/cpu/s390/continuationEntry_s390.hpp index e4e611d2b15..15b1347ce0a 100644 --- a/src/hotspot/cpu/s390/continuationEntry_s390.hpp +++ b/src/hotspot/cpu/s390/continuationEntry_s390.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 SAP SE. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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,8 +26,11 @@ #ifndef CPU_S390_CONTINUATIONENTRY_S390_HPP #define CPU_S390_CONTINUATIONENTRY_S390_HPP +#include "runtime/frame.hpp" + class ContinuationEntryPD { - // empty + // This is needed to position the ContinuationEntry at the unextended sp of the entry frame + frame::z_abi_160_base _abi; }; #endif // CPU_S390_CONTINUATIONENTRY_S390_HPP diff --git a/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp b/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp index 1d4e3c2439d..58ff8f0d194 100644 --- a/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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,22 +26,28 @@ #ifndef CPU_S390_CONTINUATIONENTRY_S390_INLINE_HPP #define CPU_S390_CONTINUATIONENTRY_S390_INLINE_HPP +#include "oops/method.inline.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/registerMap.hpp" +#include "utilities/macros.hpp" #include "runtime/continuationEntry.hpp" -// TODO: Implement - inline frame ContinuationEntry::to_frame() const { - Unimplemented(); - return frame(); + static CodeBlob* cb = CodeCache::find_blob_fast(entry_pc()); + assert(cb != nullptr, ""); + assert(cb->as_nmethod()->method()->is_continuation_enter_intrinsic(), ""); + return frame(entry_sp(), entry_pc(), entry_sp(), entry_fp(), cb); } inline intptr_t* ContinuationEntry::entry_fp() const { - Unimplemented(); - return nullptr; + return (intptr_t*)((address)this + size()); } inline void ContinuationEntry::update_register_map(RegisterMap* map) const { - Unimplemented(); + // No register map update needed for s390. + // In the Java calling convention on s390, all registers are volatile (caller-saved), + // so there are no non-volatile (callee-saved) registers that need to be tracked + // in the register map for continuation entry frames. } #endif // CPU_S390_CONTINUATIONENTRY_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index 1102a745ac0..2f7660052c0 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,98 +30,316 @@ #include "runtime/frame.hpp" #include "runtime/frame.inline.hpp" +inline void patch_callee_link(const frame& f, intptr_t* fp) { + *ContinuationHelper::Frame::callee_link_address(f) = fp; +} + +inline void patch_callee_link_relative(const frame& f, intptr_t* fp) { + intptr_t* la = (intptr_t*)ContinuationHelper::Frame::callee_link_address(f); + intptr_t new_value = fp - la; + *la = new_value; +} + inline void FreezeBase::set_top_frame_metadata_pd(const frame& hf) { - Unimplemented(); + stackChunkOop chunk = _cont.tail(); + assert(chunk->is_in_chunk(hf.sp()), "hf.sp()=" PTR_FORMAT, p2i(hf.sp())); + + hf.own_abi()->return_pc = (uint64_t)hf.pc(); + if (hf.is_interpreted_frame()) { + patch_callee_link_relative(hf, hf.fp()); + } else { +#ifdef ASSERT + // See also FreezeBase::patch_pd() + patch_callee_link(hf, (intptr_t*)badAddress); +#endif + } } template inline frame FreezeBase::sender(const frame& f) { - Unimplemented(); - return frame(); + assert(FKind::is_instance(f), ""); + + if (FKind::interpreted) { + return frame(f.sender_sp(), f.sender_pc(), f.interpreter_frame_sender_sp()); + } + + intptr_t* sender_sp = f.sender_sp(); + address sender_pc = f.sender_pc(); + assert(sender_sp != f.sp(), "must have changed"); + int slot = 0; + CodeBlob* sender_cb = CodeCache::find_blob_and_oopmap(sender_pc, slot); + return sender_cb != nullptr + ? frame(sender_sp, sender_sp, nullptr, sender_pc, sender_cb, slot == -1 ? nullptr : sender_cb->oop_map_for_slot(slot, sender_pc)) + : frame(sender_sp, sender_pc, sender_sp); } template frame FreezeBase::new_heap_frame(frame& f, frame& caller) { - Unimplemented(); - return frame(); + assert(FKind::is_instance(f), ""); + intptr_t *sp, *fp; + if (FKind::interpreted) { + intptr_t locals_offset = *f.addr_at(_z_ijava_idx(locals)); + + // If the caller.is_empty(), i.e. we're freezing into an empty chunk, then we set + // the chunk's argsize in finalize_freeze and make room for it above the unextended_sp + // See also comment on StackChunkFrameStream::interpreter_frame_size() + + int overlap = + (caller.is_interpreted_frame() || caller.is_empty()) + ? ContinuationHelper::InterpretedFrame::stack_argsize(f) + frame::metadata_words_at_top + : 0; + + // Calculate the new frame's FP in the heap chunk. + // Starting from caller's unextended_sp, we: + // - subtract 1 for the z_parent_ijava_frame_abi (which sits just below the locals) + // - subtract locals_offset (distance from FP to locals in the original frame) + // - add overlap (to account for shared stack args when caller is interpreted or empty) + // This positions FP such that locals are correctly placed relative to the caller's frame. + fp = caller.unextended_sp() - 1 - locals_offset + overlap; + + // esp points one slot below the last argument + intptr_t* x86_64_like_unextended_sp = f.interpreter_frame_esp() + 1 - frame::metadata_words_at_top; + + sp = fp - (f.fp() - x86_64_like_unextended_sp); + assert (sp <= fp && (fp <= caller.unextended_sp() || caller.is_interpreted_frame()), + "sp=" PTR_FORMAT " fp=" PTR_FORMAT " caller.unextended_sp()=" PTR_FORMAT " caller.is_interpreted_frame()=%d", + p2i(sp), p2i(fp), p2i(caller.unextended_sp()), caller.is_interpreted_frame()); + caller.set_sp(fp); + + assert(_cont.tail()->is_in_chunk(sp), ""); + + frame hf(sp, sp, fp, f.pc(), nullptr, nullptr, true /* on_heap */); + // frame_top() and frame_bottom() read these before relativize_interpreted_frame_metadata() is called + *hf.addr_at(_z_ijava_idx(locals)) = locals_offset; + *hf.addr_at(_z_ijava_idx(esp)) = f.interpreter_frame_esp() - f.fp(); + return hf; + } else { + int fsize = FKind::size(f); + sp = caller.unextended_sp() - fsize; + if (caller.is_interpreted_frame()) { + // If the caller is interpreted, our stackargs are not supposed to overlap with it + // so we make more room by moving sp down by argsize + int argsize = FKind::stack_argsize(f); + sp -= argsize + frame::metadata_words_at_top; + } + fp = sp + fsize; + caller.set_sp(fp); + + assert(_cont.tail()->is_in_chunk(sp), ""); + + return frame(sp, sp, fp, f.pc(), nullptr, nullptr, true /* on_heap */); + } } void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { - Unimplemented(); + // Nothing to do on s390 and ppc. On x86/aarch64/riscv, the unextended_sp is stored + // in interpreter_frame_last_sp and needs to be restored from there. On s390/ppc, + // the frame structure doesn't have interpreter_frame_last_sp; instead, the unextended_sp + // is directly maintained in the frame and doesn't need adjustment. } inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { - Unimplemented(); + // Nothing to do. We don't save a last sp because we cannot use sp as esp. + // Instead the top frame is trimmed when making an i2i call. The original + // top_frame_sp is set when the frame is pushed (see generate_fixed_frame()). + // An interpreter top frame that was just thawed is resized to top_frame_sp by the + // resume adapter (see generate_cont_resume_interpreter_adapter()). So the assertion is + // false, if we freeze again right after thawing as we do when redoing a vm call wasn't + // successful. + assert(_thread->interp_redoing_vm_call() || + ((intptr_t*)f.at_relative(_z_ijava_idx(top_frame_sp)) == f.unextended_sp()), + "top_frame_sp:" PTR_FORMAT " usp:" PTR_FORMAT, f.at_relative(_z_ijava_idx(top_frame_sp)), p2i(f.unextended_sp())); } inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { - Unimplemented(); + intptr_t* vfp = f.fp(); + intptr_t* hfp = hf.fp(); + assert(f.fp() > (intptr_t*)f.interpreter_frame_esp(), ""); + + // There is alignment padding between vfp and f's locals array in the original + // frame, because we freeze the padding (see recurse_freeze_interpreted_frame) + // in order to keep the same relativized locals pointer, we don't need to change it here. + + // Make sure that monitors is already relativized. + assert(hf.at_absolute(_z_ijava_idx(monitors)) <= -(frame::z_ijava_state_size / wordSize), ""); + // Make sure that esp is already relativized. + assert(hf.at_absolute(_z_ijava_idx(esp)) <= hf.at_absolute(_z_ijava_idx(monitors)), ""); + // top_frame_sp is already relativized + + // hfp == hf.sp() + (f.fp() - f.sp()) is not true on ppc because the stack frame has room for + // the maximal expression stack and the expression stack in the heap frame is trimmed. + assert(hf.fp() == hf.interpreter_frame_esp() + (f.fp() - f.interpreter_frame_esp()), ""); + assert(hf.fp() <= (intptr_t*)hf.at(_z_ijava_idx(locals)), ""); } inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { - Unimplemented(); + if (caller.is_interpreted_frame()) { + assert(!caller.is_empty(), ""); + patch_callee_link_relative(caller, caller.fp()); + } +#ifdef ASSERT + else { + // For compiled frames the back link is actually redundant. It gets computed + // as unextended_sp + frame_size. + + // Note a difference from x86_64: the link is not made relative if the caller + // is a compiled frame because there rbp is used as a non-volatile register by + // c1/c2 so it could be a computed value local to the caller. + + // See also: + // - FreezeBase::set_top_frame_metadata_pd + // - StackChunkFrameStream::fp() + // - UseContinuationFastPath: compiled frames are copied in a batch w/o patching the back link. + // The backlinks are restored when thawing (see Thaw::patch_caller_links()) + patch_callee_link(hf, (intptr_t*)badAddress); + } +#endif } inline void FreezeBase::patch_pd_unused(intptr_t* sp) { - Unimplemented(); } inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { - Unimplemented(); + // Nothing to do. The backchain is reconstructed when thawing (see Thaw::patch_caller_links()) } inline intptr_t* AnchorMark::anchor_mark_set_pd() { - Unimplemented(); - return nullptr; + // Nothing to do on s390 because the interpreter does not use SP as expression stack pointer. + // Instead there is a dedicated register Z_esp which is not affected by VM calls. + return _top_frame.sp(); } inline void AnchorMark::anchor_mark_clear_pd() { - Unimplemented(); + // Nothing to do. See anchor_mark_set_pd(). } inline frame ThawBase::new_entry_frame() { - Unimplemented(); - return frame(); + intptr_t* sp = _cont.entrySP(); + return frame(sp, _cont.entryPC(), sp, _cont.entryFP()); } template frame ThawBase::new_stack_frame(const frame& hf, frame& caller, bool bottom) { - Unimplemented(); - return frame(); + assert(FKind::is_instance(hf), ""); + + assert(is_aligned(caller.fp(), frame::frame_alignment), PTR_FORMAT, p2i(caller.fp())); + // caller.sp() can be unaligned. This is fixed below. + if (FKind::interpreted) { + // Note: we have to overlap with the caller, at least if it is interpreted, to match the + // max_thawing_size calculation during freeze. See also comment above. + intptr_t* heap_sp = hf.unextended_sp(); + const int fsize = ContinuationHelper::InterpretedFrame::frame_bottom(hf) - hf.unextended_sp(); + const int overlap = !caller.is_interpreted_frame() ? 0 + : ContinuationHelper::InterpretedFrame::stack_argsize(hf) + frame::metadata_words_at_top; + intptr_t* frame_sp = caller.unextended_sp() + overlap - fsize; + intptr_t* fp = frame_sp + (hf.fp() - heap_sp); + // align fp + int padding = fp - align_down(fp, frame::frame_alignment); + fp -= padding; + // alignment of sp is done by callee or in finish_thaw() + frame_sp -= padding; + + // On s390 esp points to the first free slot on the expression stack (see frame_s390.hpp). + // The assertion verifies that frame_sp + metadata_words_at_top points to the slot above esp, + // which corresponds to the last parameter position. + DEBUG_ONLY(intptr_t* esp = fp + *hf.addr_at(_z_ijava_idx(esp));) + assert(frame_sp + frame::metadata_words_at_top == esp+1, " frame_sp=" PTR_FORMAT " esp=" PTR_FORMAT, p2i(frame_sp), p2i(esp)); + caller.set_sp(fp); + frame f(frame_sp, hf.pc(), frame_sp, fp); + // we need to set the locals so that the caller of new_stack_frame() can call + // ContinuationHelper::InterpretedFrame::frame_bottom + // copy relativized locals from the heap frame + *f.addr_at(_z_ijava_idx(locals)) = *hf.addr_at(_z_ijava_idx(locals)); + + return f; + } else { + int fsize = FKind::size(hf); + int argsize = FKind::stack_argsize(hf); + intptr_t* frame_sp = caller.sp() - fsize; + + if ((bottom && argsize > 0) || caller.is_interpreted_frame()) { + assert(!_should_patch_caller_pc, "what??"); + _should_patch_caller_pc = caller.is_interpreted_frame(); + frame_sp -= argsize + frame::metadata_words_at_top; + frame_sp = align_down(frame_sp, frame::alignment_in_bytes); + caller.set_sp(frame_sp + fsize); + } + + assert(hf.cb() != nullptr, ""); + assert(hf.oop_map() != nullptr, ""); + intptr_t* fp = frame_sp + fsize; + return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); + } } inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { - Unimplemented(); + // Make sure that monitors is still relativized. + assert(f.at_absolute(_z_ijava_idx(monitors)) <= -(frame::z_ijava_state_size / wordSize), ""); + // Make sure that esp is still relativized. + assert(f.at_absolute(_z_ijava_idx(esp)) <= f.at_absolute(_z_ijava_idx(monitors)), ""); + // Keep top_frame_sp relativized. } inline intptr_t* ThawBase::align(const frame& hf, intptr_t* frame_sp, frame& caller, bool bottom) { - Unimplemented(); + // Unused. Alignment is done directly in new_stack_frame() / finish_thaw(). return nullptr; } inline void ThawBase::patch_pd(frame& f, const frame& caller) { - Unimplemented(); + patch_callee_link(caller, caller.fp()); + // Prevent assertion if f gets deoptimized right away before it's fully initialized + f.mark_not_fully_initialized(); } inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { - Unimplemented(); + assert(f.own_abi()->callers_sp == (uint64_t)caller_sp, "should have been fixed by patch_caller_links"); } inline intptr_t* ThawBase::push_cleanup_continuation() { - Unimplemented(); - return nullptr; + frame enterSpecial = new_entry_frame(); + frame::z_common_abi* enterSpecial_abi = (frame::z_common_abi*)enterSpecial.sp(); + + enterSpecial_abi->return_pc = (intptr_t)ContinuationEntry::cleanup_pc(); + + log_develop_trace(continuations, preempt)("push_cleanup_continuation enterSpecial sp: " INTPTR_FORMAT " cleanup pc: " INTPTR_FORMAT, + p2i(enterSpecial_abi), + p2i(ContinuationEntry::cleanup_pc())); + + return enterSpecial.sp(); } inline intptr_t* ThawBase::push_preempt_adapter() { - Unimplemented(); - return nullptr; + frame enterSpecial = new_entry_frame(); + frame::z_common_abi* enterSpecial_abi = (frame::z_common_abi*)enterSpecial.sp(); + + enterSpecial_abi->return_pc = (intptr_t)StubRoutines::cont_preempt_stub(); + + log_develop_trace(continuations, preempt)("push_preempt_adapter enterSpecial sp: " INTPTR_FORMAT " adapter pc: " INTPTR_FORMAT, + p2i(enterSpecial_abi), + p2i(StubRoutines::cont_preempt_stub())); + + return enterSpecial.sp(); } template inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { - Unimplemented(); + for (intptr_t* callers_sp; sp < bottom; sp = callers_sp) { + address pc = (address)((frame::z_java_abi*) sp)->return_pc; + assert(pc != nullptr, ""); + // see ThawBase::patch_return() which gets called just before + bool is_entry_frame = pc == StubRoutines::cont_returnBarrier() || pc == _cont.entryPC(); + if (is_entry_frame) { + callers_sp = _cont.entryFP(); + } else { + assert(!Interpreter::contains(pc), "sp:" PTR_FORMAT " pc:" PTR_FORMAT, p2i(sp), p2i(pc)); + CodeBlob* cb = CodeCache::find_blob(pc); + callers_sp = sp + cb->frame_size(); + } + // set the back link + ((frame::z_java_abi*) sp)->callers_sp = (intptr_t) callers_sp; + } } inline void ThawBase::prefetch_chunk_pd(void* start, int size) { - Unimplemented(); + // TODO: implement in future; } #endif // CPU_S390_CONTINUATION_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp index fb7d998c458..11944a8f040 100644 --- a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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,108 +28,133 @@ #include "runtime/continuationHelper.hpp" -// TODO: Implement - -template -static inline intptr_t** link_address(const frame& f) { - Unimplemented(); - return nullptr; -} - static inline void patch_return_pc_with_preempt_stub(frame& f) { - Unimplemented(); + if (f.is_runtime_frame()) { + // Patch the pc of the now old last Java frame (we already set the anchor to enterSpecial) + // so that when target returns to Java it will actually return to the preempt cleanup stub. + // We step over the runtime stub frame and patch the return PC in the caller's frame. + intptr_t* caller_sp = f.sp() + f.cb()->frame_size(); + frame::z_common_abi* abi = (frame::z_common_abi*)caller_sp; + abi->return_pc = (uint64_t)StubRoutines::cont_preempt_stub(); + } else { + // The target will check for preemption once it returns to the interpreter + // or the native wrapper code and will manually jump to the preempt stub. + JavaThread *thread = JavaThread::current(); + DEBUG_ONLY(Method* m = f.is_interpreted_frame() ? f.interpreter_frame_method() : f.cb()->as_nmethod()->method();) + assert(m->is_object_wait0() || thread->interp_at_preemptable_vmcall_cnt() > 0, + "preemptable VM call not using call_VM_preemptable"); + thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub()); + } } inline int ContinuationHelper::frame_align_words(int size) { - Unimplemented(); + // S390 requires 8-byte (1-word) frame alignment, not 16-byte like other platforms. + // Because frames are already 8-byte aligned, no additional padding words are needed. + // Other platforms (x86, aarch64, ppc) return size & 1 to ensure 16-byte alignment, + // but s390's 8-byte alignment requirement is already satisfied. return 0; } -inline intptr_t* ContinuationHelper::frame_align_pointer(intptr_t* sp) { - Unimplemented(); - return nullptr; +inline intptr_t* ContinuationHelper::frame_align_pointer(intptr_t* p) { + return align_down(p, frame::frame_alignment); } template inline void ContinuationHelper::update_register_map(const frame& f, RegisterMap* map) { - Unimplemented(); + // All registers are considered volatile and saved in the caller (Java) frame if needed. + // No register map update required for s390. } inline void ContinuationHelper::update_register_map_with_callee(const frame& f, RegisterMap* map) { - Unimplemented(); + // All registers are considered volatile and saved in the caller (Java) frame if needed. + // No register map update required for s390. } inline void ContinuationHelper::push_pd(const frame& f) { - Unimplemented(); + f.own_abi()->callers_sp = (uint64_t)f.fp(); } inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, ContinuationEntry* cont) { - Unimplemented(); + // No frame pointer update needed for s390. + // Unlike x86/aarch64, s390 doesn't require setting last_Java_fp in the anchor. } inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { - Unimplemented(); + // No frame pointer update needed for s390. + // Unlike x86/aarch64, s390 doesn't require setting last_Java_fp in the anchor. } #ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { - Unimplemented(); - return false; + intptr_t* sp = f.sp(); + address pc = *(address*)(sp - frame::sender_sp_ret_address_offset()); + intptr_t* fp = (intptr_t*)f.own_abi()->callers_sp; + assert(f.raw_pc() == pc, "f.ra_pc: " INTPTR_FORMAT " actual: " INTPTR_FORMAT, p2i(f.raw_pc()), p2i(pc)); + assert(f.fp() == fp, "f.fp: " INTPTR_FORMAT " actual: " INTPTR_FORMAT, p2i(f.fp()), p2i(fp)); + return f.raw_pc() == pc && f.fp() == fp; } #endif inline intptr_t** ContinuationHelper::Frame::callee_link_address(const frame& f) { - Unimplemented(); - return nullptr; -} - -template -static inline intptr_t* real_fp(const frame& f) { - Unimplemented(); - return nullptr; + return (intptr_t**)&f.own_abi()->callers_sp; } inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const frame& f) { - Unimplemented(); - return nullptr; + return (address*)&f.callers_abi()->return_pc; } inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { - Unimplemented(); + intptr_t* sp = caller.unextended_sp(); + if (!f.is_heap_frame() && caller.is_interpreted_frame()) { + // When the caller is an interpreted frame, we need to use the caller's top_frame_sp + // instead of unextended_sp. This is because the interpreter resizes the caller's + // frame before making a call + sp = (intptr_t*)caller.at_relative(_z_ijava_idx(top_frame_sp)); + } + assert(f.is_interpreted_frame(), ""); + assert(f.is_heap_frame() || is_aligned(sp, frame::alignment_in_bytes), ""); + intptr_t* la = f.addr_at(_z_ijava_idx(sender_sp)); + *la = f.is_heap_frame() ? (intptr_t)(sp - f.fp()) : (intptr_t)sp; } inline address* ContinuationHelper::Frame::return_pc_address(const frame& f) { - Unimplemented(); - return nullptr; + return (address*)&f.callers_abi()->return_pc; } inline address ContinuationHelper::Frame::real_pc(const frame& f) { - Unimplemented(); - return nullptr; + return (address)f.own_abi()->return_pc; } inline void ContinuationHelper::Frame::patch_pc(const frame& f, address pc) { - Unimplemented(); + f.own_abi()->return_pc = (uint64_t)pc; } inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, InterpreterOopMap* mask) { // inclusive; this will be copied with the frame - Unimplemented(); - return nullptr; + int expression_stack_sz = expression_stack_size(f, mask); + intptr_t* res = (intptr_t*)f.interpreter_frame_monitor_end() - expression_stack_sz; + assert(res <= (intptr_t*)f.ijava_state() - expression_stack_sz, + "res=" PTR_FORMAT " f.ijava_state()=" PTR_FORMAT " expression_stack_sz=%d", + p2i(res), p2i(f.ijava_state()), expression_stack_sz); + assert(res >= f.unextended_sp(), + "res: " INTPTR_FORMAT " ijava_state: " INTPTR_FORMAT " esp: " INTPTR_FORMAT " unextended_sp: " INTPTR_FORMAT " expression_stack_size: %d", + p2i(res), p2i(f.ijava_state()), f.ijava_state()->esp, p2i(f.unextended_sp()), expression_stack_sz); + return res; } inline intptr_t* ContinuationHelper::InterpretedFrame::frame_bottom(const frame& f) { // exclusive; this will not be copied with the frame - Unimplemented(); - return nullptr; + return (intptr_t*)f.at_relative(_z_ijava_idx(locals)) + 1; // exclusive; this will not be copied with the frame } inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, int callee_argsize, bool callee_interpreted) { - Unimplemented(); - return nullptr; + intptr_t* pseudo_unextended_sp = f.interpreter_frame_esp() + 1 - frame::metadata_words_at_top; + // callee_argsize includes metadata (frame::metadata_words_at_top). + // When the callee is interpreted, we add callee_argsize to account for the arguments + // that are part of the caller's frame but logically belong to the callee. + return pseudo_unextended_sp + (callee_interpreted ? callee_argsize : 0); } inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { - Unimplemented(); - return nullptr; + return f.fp(); } #endif // CPU_S390_CONTINUATIONHELPER_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/frame_s390.cpp b/src/hotspot/cpu/s390/frame_s390.cpp index b602d0adce5..af4c670132a 100644 --- a/src/hotspot/cpu/s390/frame_s390.cpp +++ b/src/hotspot/cpu/s390/frame_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, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -54,6 +54,10 @@ void RegisterMap::check_location_valid() { // Profiling/safepoint support bool frame::safe_for_sender(JavaThread *thread) { + if (is_heap_frame()) { + return true; + } + address sp = (address)_sp; address fp = (address)_fp; address unextended_sp = (address)_unextended_sp; @@ -120,6 +124,13 @@ bool frame::safe_for_sender(JavaThread *thread) { intptr_t* sender_sp = (intptr_t*) fp; address sender_pc = (address) sender_abi->return_pc; + if (Continuation::is_return_barrier_entry(sender_pc)) { + // If our sender_pc is the return barrier, then our "real" sender is the continuation entry + frame s = Continuation::continuation_bottom_sender(thread, *this, sender_sp); + sender_sp = s.sp(); + sender_pc = s.pc(); + } + // We must always be able to find a recognizable pc. CodeBlob* sender_blob = CodeCache::find_blob(sender_pc); if (sender_blob == nullptr) { @@ -192,7 +203,8 @@ void frame::interpreter_frame_set_locals(intptr_t* locs) { // sender_sp intptr_t* frame::interpreter_frame_sender_sp() const { - return sender_sp(); + assert(is_interpreted_frame(), "interpreted frame expected"); + return (intptr_t*)at(_z_ijava_idx(sender_sp)); } frame frame::sender_for_entry_frame(RegisterMap *map) const { @@ -244,16 +256,52 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { frame fr(jfa->last_Java_sp(), jfa->last_Java_pc()); return fr; + } +#if defined(ASSERT) +static address get_register_address_in_stub(const frame& stub_fr, VMReg reg) { + RegisterMap map(nullptr, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + stub_fr.oop_map()->update_register_map(&stub_fr, &map); + return map.location(reg, stub_fr.sp()); +} +#endif + JavaThread** frame::saved_thread_address(const frame& f) { - Unimplemented(); - return nullptr; + CodeBlob* cb = f.cb(); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + JavaThread** thread_addr; +#ifdef COMPILER1 + if (cb == Runtime1::blob_for(StubId::c1_monitorenter_id) || + cb == Runtime1::blob_for(StubId::c1_monitorenter_nofpu_id)) { + thread_addr = (JavaThread**)(f.sp() + Runtime1::runtime_blob_current_thread_offset(f)); + } else +#endif + { + // c2 only saves Z_fp in the stub frame so nothing to do. + thread_addr = nullptr; + } + assert(get_register_address_in_stub(f, SharedRuntime::thread_register()) == (address)thread_addr, "wrong thread address"); + return thread_addr; } frame frame::sender_for_interpreter_frame(RegisterMap *map) const { - // Pass callers sender_sp as unextended_sp. - return frame(sender_sp(), sender_pc(), (intptr_t*)(ijava_state()->sender_sp)); + // This is the sp before any possible extension (adapter/locals). + intptr_t* unextended_sp = interpreter_frame_sender_sp(); + address sender_pc = this->sender_pc(); + 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 frame(sender_sp(), sender_pc, unextended_sp); } void frame::patch_pc(Thread* thread, address pc) { @@ -284,7 +332,7 @@ void frame::patch_pc(Thread* thread, address pc) { #ifdef ASSERT { - frame f(this->sp(), pc, this->unextended_sp()); + frame f(sp(), unextended_sp(), fp(), pc, cb(), oop_map(), is_heap_frame()); assert(f.is_deoptimized_frame() == this->is_deoptimized_frame() && f.pc() == this->pc() && f.raw_pc() == this->raw_pc(), "must be (f.is_deoptimized_frame(): %d this->is_deoptimized_frame(): %d " "f.pc(): " INTPTR_FORMAT " this->pc(): " INTPTR_FORMAT " f.raw_pc(): " INTPTR_FORMAT " this->raw_pc(): " INTPTR_FORMAT ")", @@ -648,6 +696,8 @@ extern "C" void bt_max(intptr_t *start_sp, intptr_t *top_pc, int max_frames) { } #if !defined(PRODUCT) +#define DESCRIBE_ADDRESS_MAGIC(name) \ + values.describe(frame_no, (intptr_t*)&ijava_state()->name, #name "_number_debug"); #define DESCRIBE_ADDRESS(name) \ values.describe(frame_no, (intptr_t*)&ijava_state()->name, #name); @@ -656,25 +706,38 @@ void frame::describe_pd(FrameValues& values, int frame_no) { if (is_interpreted_frame()) { // Describe z_ijava_state elements. DESCRIBE_ADDRESS(method); + DESCRIBE_ADDRESS(mirror); DESCRIBE_ADDRESS(locals); DESCRIBE_ADDRESS(monitors); DESCRIBE_ADDRESS(cpoolCache); DESCRIBE_ADDRESS(bcp); - DESCRIBE_ADDRESS(mdx); DESCRIBE_ADDRESS(esp); - DESCRIBE_ADDRESS(sender_sp); + DESCRIBE_ADDRESS(mdx); DESCRIBE_ADDRESS(top_frame_sp); + DESCRIBE_ADDRESS(sender_sp); DESCRIBE_ADDRESS(oop_tmp); DESCRIBE_ADDRESS(lresult); DESCRIBE_ADDRESS(fresult); + DESCRIBE_ADDRESS_MAGIC(magic); + } + + if (is_java_frame() || Continuation::is_continuation_enterSpecial(*this)) { + intptr_t* ret_pc_loc = (intptr_t*)&own_abi()->return_pc; + address ret_pc = *(address*)ret_pc_loc; + values.describe(frame_no, ret_pc_loc, + Continuation::is_return_barrier_entry(ret_pc) ? "return address (return barrier)" : "return address"); } } #endif // !PRODUCT intptr_t *frame::initial_deoptimization_info() { - // Used to reset the saved FP. - return fp(); + // `this` is the caller of the deoptee. We want to trim it, if compiled, to + // unextended_sp. This is necessary if the deoptee frame is the bottom frame + // of a continuation on stack (more frames could be in a StackChunk) as it + // will pop its stack args. Otherwise the recursion in + // FreezeBase::recurse_freeze_java_frame() would not stop at the bottom frame. + return is_compiled_frame() ? unextended_sp() : sp(); } BasicObjectLock* frame::interpreter_frame_monitor_end() const { diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index bcdeec43e1a..8a99bdb8df5 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -130,6 +130,7 @@ enum { z_native_abi_size = sizeof(z_native_abi), + z_abi_160_base_size = sizeof(z_abi_160_base), z_abi_160_size = sizeof(z_abi_160_base) }; @@ -442,6 +443,14 @@ private: + + #ifdef ASSERT + enum special_backlink_values : uint64_t { + NOT_FULLY_INITIALIZED = 0xDEADBEEF8 + }; + bool is_fully_initialized() const { return (uint64_t)_fp != NOT_FULLY_INITIALIZED; } +#endif // ASSERT + // STACK: // ... // [THIS_FRAME] <-- this._sp (stack pointer for this frame) @@ -452,10 +461,16 @@ // NOTE: Stack pointer is now held in the base class, so remove it from here. // Needed by deoptimization. - intptr_t* _unextended_sp; + union { + intptr_t* _unextended_sp; + int _offset_unextended_sp; // for use in stack-chunk frames + }; // Frame pointer for this frame. - intptr_t* _fp; + union { + intptr_t* _fp; // frame pointer + int _offset_fp; // relative frame pointer for use in stack-chunk frames + }; public: @@ -464,17 +479,25 @@ // Accessors inline intptr_t* fp() const { assert_absolute(); return _fp; } + void set_fp(intptr_t* newfp) { _fp = newfp; } + int offset_fp() const { assert_offset(); return _offset_fp; } + void set_offset_fp(int value) { assert_on_heap(); _offset_fp = value; } + + // Mark a frame as not fully initialized. Must not be used for frames in the valid back chain. + void mark_not_fully_initialized() const { DEBUG_ONLY(own_abi()->callers_sp = NOT_FULLY_INITIALIZED;) } private: // Initialize frame members (_pc and _sp must be given) inline void setup(); - // Constructors - public: + + // Constructors + inline frame(intptr_t* sp, intptr_t* fp, address pc); // To be used, if sp was not extended to match callee's calling convention. inline frame(intptr_t* sp, address pc, intptr_t* unextended_sp = nullptr, intptr_t* fp = nullptr, CodeBlob* cb = nullptr); + inline frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map, bool on_heap); inline frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map = nullptr); // Access frame via stack pointer. @@ -495,11 +518,8 @@ // template interpreter state inline z_ijava_state* ijava_state_unchecked() const; - private: - - inline z_ijava_state* ijava_state() const; - public: + inline z_ijava_state* ijava_state() const; inline intptr_t* interpreter_frame_esp() const; // Where z_ijava_state.esp is saved. @@ -542,22 +562,17 @@ unsigned long flags, int max_frames = 0); enum { - // This enum value specifies the offset from the pc remembered by - // call instructions to the location where control returns to - // after a normal return. Most architectures remember the return - // location directly, i.e. the offset is zero. This is the case - // for z/Architecture, too. - // - // Normal return address is the instruction following the branch. - pc_return_offset = 0, - metadata_words = 0, + // size, in words, of frame metadata (e.g. pc and link) + metadata_words = sizeof(z_java_abi) >> LogBytesPerWord, metadata_words_at_bottom = 0, - metadata_words_at_top = 0, - frame_alignment = 16, + metadata_words_at_top = sizeof(z_java_abi) >> LogBytesPerWord, + // in bytes + frame_alignment = 8, // size, in words, of maximum shift in frame position due to alignment - align_wiggle = 1 + align_wiggle = 0 }; - static jint interpreter_frame_expression_stack_direction() { return -1; } + // returns the sending frame, without applying any barriers + inline frame sender_raw(RegisterMap* map) const; #endif // CPU_S390_FRAME_S390_HPP diff --git a/src/hotspot/cpu/s390/frame_s390.inline.hpp b/src/hotspot/cpu/s390/frame_s390.inline.hpp index 6fcd36c57d1..e31b0d5a426 100644 --- a/src/hotspot/cpu/s390/frame_s390.inline.hpp +++ b/src/hotspot/cpu/s390/frame_s390.inline.hpp @@ -26,7 +26,8 @@ #ifndef CPU_S390_FRAME_S390_INLINE_HPP #define CPU_S390_FRAME_S390_INLINE_HPP -#include "code/codeCache.hpp" +#include "code/codeBlob.inline.hpp" +#include "code/codeCache.inline.hpp" #include "code/vmreg.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" @@ -44,14 +45,25 @@ inline void frame::setup() { _cb = CodeCache::find_blob(_pc); } - if (_fp == nullptr) { - _fp = (intptr_t*)own_abi()->callers_sp; - } - if (_unextended_sp == nullptr) { _unextended_sp = _sp; } + if (_fp == nullptr) { + // The back link for compiled frames on the heap is not valid + if (is_heap_frame()) { + // fp for interpreted frames should have been derelativized and passed to the constructor + assert(is_compiled_frame() + || is_native_frame() // native wrapper (nmethod) for j.l.Object::wait0 + || is_runtime_frame(), // e.g. Runtime1::monitorenter, SharedRuntime::complete_monitor_locking_C + "sp:" PTR_FORMAT " fp:" PTR_FORMAT " name:%s", p2i(_sp), p2i(_unextended_sp + _cb->frame_size()), _cb->name()); + // The back link for compiled frames on the heap is invalid. + _fp = _unextended_sp + _cb->frame_size(); + } else { + _fp = (intptr_t *) own_abi()->callers_sp; + } + } + // When thawing continuation frames the _unextended_sp passed to the constructor is not aligend assert(_on_heap || (is_aligned(_sp, alignment_in_bytes) && is_aligned(_fp, alignment_in_bytes)), "invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp)); @@ -70,7 +82,12 @@ inline void frame::setup() { } } - // assert(_on_heap || is_aligned(_sp, frame::frame_alignment), "SP must be 8-byte aligned"); + // Continuation frames on the java heap are not aligned. + // When thawing interpreted frames the sp can be unaligned (see new_stack_frame()). + assert(_on_heap || + ((is_aligned(_sp, alignment_in_bytes) || is_interpreted_frame()) && + (is_aligned(_fp, alignment_in_bytes) || !is_fully_initialized())), + "invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp)); } // Constructors @@ -87,11 +104,26 @@ inline frame::frame(intptr_t* sp, address pc, intptr_t* unextended_sp, intptr_t* inline frame::frame(intptr_t* sp) : frame(sp, nullptr) {} +inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) + : _sp(sp), _pc(pc), _cb(nullptr), _oop_map(nullptr), _deopt_state(unknown), + _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(nullptr), _fp(fp) { + setup(); +} + inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map) :_sp(sp), _pc(pc), _cb(cb), _oop_map(oop_map), _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { setup(); } +inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map, bool on_heap) + :_sp(sp), _pc(pc), _cb(cb), _oop_map(oop_map), _on_heap(on_heap), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { + // In thaw, non-heap frames use this constructor to pass oop_map. I don't know why. + assert(_on_heap || _cb != nullptr, "these frames are always heap frames"); + if (cb != nullptr) { + setup(); + } +} + // Generic constructor. Used by pns() in debug.cpp only #ifndef PRODUCT inline frame::frame(void* sp, void* pc, void* unextended_sp) @@ -295,11 +327,11 @@ inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { } inline oop frame::saved_oop_result(RegisterMap* map) const { - return *((oop*) map->location(Z_R2->as_VMReg(), nullptr)); // R2 is return register. + return *((oop*) map->location(Z_R2->as_VMReg(), sp())); // R2 is return register. } inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { - *((oop*) map->location(Z_R2->as_VMReg(), nullptr)) = obj; // R2 is return register. + *((oop*) map->location(Z_R2->as_VMReg(), sp())) = obj; // R2 is return register. } inline intptr_t* frame::real_fp() const { @@ -307,40 +339,55 @@ inline intptr_t* frame::real_fp() const { } inline int frame::compiled_frame_stack_argsize() const { - Unimplemented(); - return 0; + assert(cb()->is_nmethod(), "what ?"); + return (cb()->as_nmethod()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord; } inline void frame::interpreted_frame_oop_map(InterpreterOopMap* mask) const { - Unimplemented(); + assert(mask != nullptr, ""); + Method* m = interpreter_frame_method(); + int bci = interpreter_frame_bci(); + m->mask_for(bci, mask); // OopMapCache::compute_one_oop_map(m, bci, mask); } inline int frame::sender_sp_ret_address_offset() { - Unimplemented(); - return 0; + return -(int)(_z_common_abi(return_pc) >> LogBytesPerWord); } inline void frame::set_unextended_sp(intptr_t* value) { - Unimplemented(); + _unextended_sp = value; } inline int frame::offset_unextended_sp() const { - Unimplemented(); - return 0; + assert_offset(); return _offset_unextended_sp; } inline void frame::set_offset_unextended_sp(int value) { - Unimplemented(); + assert_on_heap(); _offset_unextended_sp = value; } //------------------------------------------------------------------------------ // frame::sender inline frame frame::sender(RegisterMap* map) const { + frame result = sender_raw(map); + + if (map->process_frames() && !map->in_cont()) { + StackWatermarkSet::on_iteration(map->thread(), result); + } + + return result; +} + +inline frame frame::sender_raw(RegisterMap* map) const { // Default is we don't have to follow them. The sender_for_xxx will // update it accordingly. map->set_include_argument_oops(false); + if (map->in_cont()) { // already in an h-stack + return map->stack_chunk()->sender(*this, map); + } + if (is_entry_frame()) return sender_for_entry_frame(map); if (is_upcall_stub_frame()) return sender_for_upcall_stub_frame(map); if (is_interpreted_frame()) return sender_for_interpreter_frame(map); @@ -362,12 +409,31 @@ inline frame frame::sender_for_compiled_frame(RegisterMap *map) const { // Now adjust the map. if (map->update_map()) { // Tell GC to use argument oopmaps for some runtime stubs that need it. - map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); - if (_cb->oop_maps() != nullptr) { - OopMapSet::update_register_map(this, map); + + // For C1, some runtime stubs don't have oop maps (e.g., slow_subtype_check, + // unwind_exception), so set this flag outside of update_register_map to ensure + // the GC can handle arguments correctly even when oop_map() is null. + if (!_cb->is_nmethod()) { // compiled frames do not use callee-saved registers + map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); + if (oop_map() != nullptr) { + _oop_map->update_register_map(this, map); + } + } else { + assert(!_cb->caller_must_gc_arguments(map->thread()), ""); + assert(!map->include_argument_oops(), ""); + assert(oop_map() == nullptr || !oop_map()->has_any(OopMapValue::callee_saved_value), "callee-saved value in compiled frame"); } } + assert(sender_sp != sp(), "must have changed"); + + 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 frame(sender_sp, sender_pc); } diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index 9a401766200..d0f92cc129a 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -116,7 +116,7 @@ void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, __ z_bre(done); // Use null result as-is. __ z_tmll(value, JNIHandles::tag_mask); - __ z_btrue(tagged); // not zero + __ branch_optimized(Assembler::bcondNotAllZero, tagged); // not zero // Resolve Local handle __ access_load_at(T_OBJECT, IN_NATIVE | AS_RAW, Address(value, 0), value, tmp1, tmp2); @@ -124,7 +124,7 @@ void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, __ bind(tagged); __ testbit(value, exact_log2(JNIHandles::TypeTag::weak_global)); // test for weak tag - __ z_btrue(weak_tag); + __ branch_optimized(Assembler::bcondNotAllZero, weak_tag); // resolve global handle __ access_load_at(T_OBJECT, IN_NATIVE, Address(value, -JNIHandles::TypeTag::global), value, tmp1, tmp2); diff --git a/src/hotspot/cpu/s390/globals_s390.hpp b/src/hotspot/cpu/s390/globals_s390.hpp index 80ed6d1acc8..745a6171ca2 100644 --- a/src/hotspot/cpu/s390/globals_s390.hpp +++ b/src/hotspot/cpu/s390/globals_s390.hpp @@ -64,7 +64,7 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); -define_pd_global(bool, VMContinuations, false); +define_pd_global(bool, VMContinuations, true); define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteFrequentPairs, true); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index cc8ca7a1f47..5d86a0c3182 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -165,6 +165,109 @@ void InterpreterMacroAssembler::dispatch_via(TosState state, address *table) { // to perform additional, template interpreter specific tasks before actually // calling their MacroAssembler counterparts. +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, address entry_point, + Register arg_1, bool check_exceptions) { + if (!Continuations::enabled()) { + call_VM(oop_result, entry_point, arg_1, check_exceptions); + return; + } + call_VM_preemptable(oop_result, entry_point, arg_1, noreg /* arg_2 */, check_exceptions); +} + +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, address entry_point, + Register arg_1, Register arg_2, bool check_exceptions) { + if (!Continuations::enabled()) { + call_VM(oop_result, entry_point, arg_1, arg_2, check_exceptions); + return; + } + + Label resume_pc, not_preempted; + Register tmp = Z_R1_scratch; + assert(InterpreterRuntime::is_preemptable_call(entry_point), "VM call not preemptable, should use call_VM()"); + assert_different_registers(arg_1, tmp); + assert_different_registers(arg_2, tmp); + +#ifdef ASSERT + { + NearLabel L1; + asm_assert_mem8_is_zero(in_bytes(JavaThread::preempt_alternate_return_offset()), Z_thread, + "Should not have alternate return address set", 100); + // We check this counter in patch_return_pc_with_preempt_stub() during freeze. + z_asi(Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset()), 1); + z_lt(tmp, Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset())); + z_brh(L1); + stop("call_VM_preemptable_helper: should be > 0"); + bind(L1); + } +#endif // ASSERT + + lgr_if_needed(Z_ARG2, arg_1); + assert(arg_2 != Z_ARG2, "smashed argument"); + + if (arg_2 != noreg) { + lgr_if_needed(Z_ARG3, arg_2); + } + + // Force freeze slow path. + push_cont_fastpath(); + // Make VM call. In case of preemption set last_pc to the one we want to resume to. + // Note: call_VM_base will use resume_pc label to set last_Java_pc. + call_VM(noreg, entry_point, false /*check_exceptions*/, &resume_pc /* last_java_pc */); + pop_cont_fastpath(); + + +#ifdef ASSERT + { + NearLabel L; + z_asi(Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset()), -1); + z_lt(tmp, Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset())); + z_brnl(L); + stop("call_VM_preemptable_helper: should be >= 0"); + bind(L); + } +#endif // ASSERT + + // Check if preempted. + z_ltg(tmp, Address(Z_thread, JavaThread::preempt_alternate_return_offset())); + z_brz(not_preempted); + + // Preempted. Frames are already frozen on heap. + z_mvghi(Address(Z_thread, JavaThread::preempt_alternate_return_offset()), 0); + z_br(tmp); // branch to handler in Z_R1_scratch + + bind(resume_pc); // Location to resume execution + restore_after_resume(); + + bind(not_preempted); + + if (check_exceptions) { + NearLabel ok; + load_and_test_long(tmp, Address(Z_thread, Thread::pending_exception_offset())); + z_bre(ok); + load_const_optimized(tmp, StubRoutines::forward_exception_entry()); + z_br(tmp); + bind(ok); + } + + // get oop result if there is one and reset the value in the thread + if (oop_result->is_valid()) { + get_vm_result_oop(oop_result); + } +} + +void InterpreterMacroAssembler::restore_after_resume() { + if (!Continuations::enabled()) return; + load_const_optimized(Z_R1, Interpreter::cont_resume_interpreter_adapter()); + call(Z_R1); +#ifdef ASSERT + NearLabel ok; + z_cg(Z_fp, _z_common_abi(callers_sp), Z_SP); + z_bre(ok); + stop(FILE_AND_LINE ": FP is expected in Z_fp"); + bind(ok); +#endif // ASSERT +} + void InterpreterMacroAssembler::call_VM_leaf_base(address entry_point) { bool allow_relocation = true; // Fenerally valid variant. Assume code is relocated. // interpreter specific @@ -193,20 +296,20 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, Register last_ save_esp(); // super call MacroAssembler::call_VM_base(oop_result, last_java_sp, - entry_point, allow_relocation, check_exceptions); + entry_point, allow_relocation, check_exceptions, nullptr); restore_bcp(); } void InterpreterMacroAssembler::call_VM_base(Register oop_result, Register last_java_sp, address entry_point, bool allow_relocation, - bool check_exceptions) { + bool check_exceptions, Label* last_java_pc) { // interpreter specific save_bcp(); save_esp(); // super call MacroAssembler::call_VM_base(oop_result, last_java_sp, - entry_point, allow_relocation, check_exceptions); + entry_point, allow_relocation, check_exceptions, last_java_pc); restore_bcp(); } @@ -697,7 +800,7 @@ void InterpreterMacroAssembler::get_monitors(Register reg) { bind(ok); #endif // ASSERT mem2reg_opt(reg, Address(Z_fp, _z_ijava_state_neg(monitors))); - z_slag(reg, reg, Interpreter::logStackElementSize); + z_slag(reg, reg, Interpreter::logStackElementSize); // sign preserved z_agr(reg, Z_fp); } @@ -968,6 +1071,14 @@ void InterpreterMacroAssembler::remove_activation(TosState state, bool install_monitor_exception, bool notify_jvmti) { BLOCK_COMMENT("remove_activation {"); + +#ifdef ASSERT + { + asm_assert_mem8_is_zero(in_bytes(JavaThread::preempt_alternate_return_offset()), Z_thread, + "remove_activation: should not have alternate return address set", 101); + } +#endif // ASSERT + unlock_if_synchronized_method(state, throw_monitor_exception, install_monitor_exception); // Save result (push state before jvmti call and pop it afterwards) and notify jvmti. @@ -1003,6 +1114,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, verify_oop(Z_tos, state); pop_interpreter_frame(return_pc, Z_ARG2, Z_ARG3); + pop_cont_fastpath(); BLOCK_COMMENT("} remove_activation"); } @@ -1023,9 +1135,9 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { z_bru(done); bind(slow_case); - call_VM(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - monitor); + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + monitor); bind(done); } @@ -1267,7 +1379,7 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, test_method_data_pointer(mdp, profile_continue); // Record the receiver type. - record_klass_in_profile(receiver, mdp, reg2); + profile_receiver_type(receiver, mdp, 0, reg2); // 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())); @@ -1275,125 +1387,6 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } } -// This routine creates a state machine for updating the multi-row -// type profile at a virtual call site (or other type-sensitive bytecode). -// The machine visits each row (of receiver/count) until the receiver type -// is found, or until it runs out of rows. At the same time, it remembers -// the location of the first empty row. (An empty row records null for its -// receiver, and can be allocated for a newly-observed receiver type.) -// Because there are two degrees of freedom in the state, a simple linear -// search will not work; it must be a decision tree. Hence this helper -// function is recursive, to generate the required tree structured code. -// It's the interpreter, so we are trading off code space for speed. -// See below for example code. -void InterpreterMacroAssembler::record_klass_in_profile_helper( - Register receiver, Register mdp, - Register reg2, int start_row, - Label& done) { - if (TypeProfileWidth == 0) { - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - return; - } - - int last_row = VirtualCallData::row_limit() - 1; - assert(start_row <= last_row, "must be work left to do"); - // Test this row for both the receiver and for null. - // Take any of three different outcomes: - // 1. found receiver => increment count and goto done - // 2. found null => keep looking for case 1, maybe allocate this cell - // 3. found something else => keep looking for cases 1 and 2 - // Case 3 is handled by a recursive call. - for (int row = start_row; row <= last_row; row++) { - NearLabel next_test; - bool test_for_null_also = (row == start_row); - - // See if the receiver is receiver[n]. - int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); - test_mdp_data_at(mdp, recvr_offset, receiver, - (test_for_null_also ? reg2 : noreg), - next_test); - // (Reg2 now contains the receiver from the CallData.) - - // The receiver is receiver[n]. Increment count[n]. - int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); - increment_mdp_data_at(mdp, count_offset); - z_bru(done); - bind(next_test); - - if (test_for_null_also) { - Label found_null; - // Failed the equality check on receiver[n]... Test for null. - z_ltgr(reg2, reg2); - if (start_row == last_row) { - // The only thing left to do is handle the null case. - z_brz(found_null); - // Receiver did not match any saved receiver and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - z_bru(done); - bind(found_null); - break; - } - // Since null is rare, make it be the branch-taken case. - z_brz(found_null); - - // Put all the "Case 3" tests here. - record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done); - - // Found a null. Keep searching for a matching receiver, - // but remember that this is an empty (unused) slot. - bind(found_null); - } - } - - // In the fall-through case, we found no matching receiver, but we - // observed the receiver[start_row] is null. - - // Fill in the receiver field and increment the count. - int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); - set_mdp_data_at(mdp, recvr_offset, receiver); - int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); - load_const_optimized(reg2, DataLayout::counter_increment); - set_mdp_data_at(mdp, count_offset, reg2); - if (start_row > 0) { - z_bru(done); - } -} - -// Example state machine code for three profile rows: -// // main copy of decision tree, rooted at row[1] -// if (row[0].rec == rec) { row[0].incr(); goto done; } -// if (row[0].rec != nullptr) { -// // inner copy of decision tree, rooted at row[1] -// if (row[1].rec == rec) { row[1].incr(); goto done; } -// if (row[1].rec != nullptr) { -// // degenerate decision tree, rooted at row[2] -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// if (row[2].rec != nullptr) { count.incr(); goto done; } // overflow -// row[2].init(rec); goto done; -// } else { -// // remember row[1] is empty -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// row[1].init(rec); goto done; -// } -// } else { -// // remember row[0] is empty -// if (row[1].rec == rec) { row[1].incr(); goto done; } -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// row[0].init(rec); goto done; -// } -// done: - -void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register mdp, Register reg2) { - assert(ProfileInterpreter, "must be profiling"); - Label done; - - record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); - - bind (done); -} - void InterpreterMacroAssembler::profile_ret(Register return_bci, Register mdp) { if (ProfileInterpreter) { NearLabel profile_continue; @@ -1462,7 +1455,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, mdp, reg2); + profile_receiver_type(klass, mdp, 0, reg2); } update_mdp_by_constant(mdp, mdp_delta); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp index b816185b065..6921fd05ff0 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp @@ -46,7 +46,8 @@ class InterpreterMacroAssembler: public MacroAssembler { Register last_java_sp, address entry_point, bool allow_relocation, - bool check_exceptions); + bool check_exceptions, + Label *last_java_pc); // Base routine for all dispatches. void dispatch_base(TosState state, address* table, bool generate_poll = false); @@ -55,9 +56,14 @@ class InterpreterMacroAssembler: public MacroAssembler { InterpreterMacroAssembler(CodeBuffer* c) : MacroAssembler(c) {} + void restore_after_resume(); virtual void check_and_handle_popframe(Register java_thread); virtual void check_and_handle_earlyret(Register java_thread); + // Use for vthread preemption + void call_VM_preemptable(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM_preemptable(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void jump_to_entry(address entry, Register Rscratch); virtual void load_earlyret_value(TosState state); @@ -280,12 +286,6 @@ class InterpreterMacroAssembler: public MacroAssembler { Register test_value_out, Label& not_equal_continue); - void record_klass_in_profile(Register receiver, Register mdp, - Register reg2); - void record_klass_in_profile_helper(Register receiver, Register mdp, - Register reg2, int start_row, - Label& done); - void update_mdp_by_offset(Register mdp_in, int offset_of_offset); void update_mdp_by_offset(Register mdp_in, Register dataidx, int offset_of_disp); void update_mdp_by_constant(Register mdp_in, int constant); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 4d2bbe796fb..6eb14452401 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -32,6 +32,7 @@ #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.inline.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -39,6 +40,7 @@ #include "oops/compressedKlass.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/klass.inline.hpp" +#include "oops/methodData.hpp" #include "prims/methodHandles.hpp" #include "registerSaver_s390.hpp" #include "runtime/icache.hpp" @@ -1931,6 +1933,12 @@ unsigned long MacroAssembler::patched_branch(address dest_pos, unsigned long ins // Only called when binding labels (share/vm/asm/assembler.cpp) // Pass arguments as intended. Do not pre-calculate distance. void MacroAssembler::pd_patch_instruction(address branch, address target, const char* file, int line) { + + if (is_load_const(branch)) { + patch_const(branch, (long)target); + return; + } + unsigned long stub_inst; int inst_len = get_instruction(branch, &stub_inst); @@ -2248,7 +2256,8 @@ void MacroAssembler::call_VM_base(Register oop_result, Register last_java_sp, address entry_point, bool allow_relocation, - bool check_exceptions) { // Defaults to true. + bool check_exceptions, // Defaults to true. + Label *last_java_pc) { // Allow_relocation indicates, if true, that the generated code shall // be fit for code relocation or referenced data relocation. In other // words: all addresses must be considered variable. PC-relative addressing @@ -2262,7 +2271,7 @@ void MacroAssembler::call_VM_base(Register oop_result, last_java_sp = Z_SP; // Load Z_SP as SP. } - set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, Z_R1, allow_relocation); + set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, Z_R1, allow_relocation, last_java_pc); // ARG1 must hold thread address. z_lgr(Z_ARG1, Z_thread); @@ -2308,14 +2317,14 @@ void MacroAssembler::call_VM_base(Register oop_result, address entry_point, bool check_exceptions) { // Defaults to true. bool allow_relocation = true; - call_VM_base(oop_result, last_java_sp, entry_point, allow_relocation, check_exceptions); + call_VM_base(oop_result, last_java_sp, entry_point, allow_relocation, check_exceptions, nullptr); } // VM calls without explicit last_java_sp. -void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { +void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions, Label* last_java_pc) { // Call takes possible detour via InterpreterMacroAssembler. - call_VM_base(oop_result, noreg, entry_point, true, check_exceptions); + call_VM_base(oop_result, noreg, entry_point, true, check_exceptions, last_java_pc); } void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions) { @@ -2347,7 +2356,7 @@ void MacroAssembler::call_VM(Register oop_result, address entry_point, Register void MacroAssembler::call_VM_static(Register oop_result, address entry_point, bool check_exceptions) { // Call takes possible detour via InterpreterMacroAssembler. - call_VM_base(oop_result, noreg, entry_point, false, check_exceptions); + call_VM_base(oop_result, noreg, entry_point, false, check_exceptions, nullptr); } void MacroAssembler::call_VM_static(Register oop_result, address entry_point, Register arg_1, Register arg_2, @@ -2365,7 +2374,7 @@ void MacroAssembler::call_VM_static(Register oop_result, address entry_point, Re void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, bool check_exceptions) { // Call takes possible detour via InterpreterMacroAssembler. - call_VM_base(oop_result, last_java_sp, entry_point, true, check_exceptions); + call_VM_base(oop_result, last_java_sp, entry_point, true, check_exceptions, nullptr); } void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions) { @@ -3809,19 +3818,21 @@ void MacroAssembler::set_last_Java_frame(Register last_Java_sp, Register last_Ja BLOCK_COMMENT("} set_last_Java_frame"); } -void MacroAssembler::reset_last_Java_frame(bool allow_relocation) { +void MacroAssembler::reset_last_Java_frame(bool check_last_java_sp, bool allow_relocation) { BLOCK_COMMENT("reset_last_Java_frame {"); - if (allow_relocation) { - asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), - Z_thread, - "SP was not set, still zero", - 0x202); - } else { - asm_assert_mem8_isnot_zero_static(in_bytes(JavaThread::last_Java_sp_offset()), - Z_thread, - "SP was not set, still zero", - 0x202); + if (check_last_java_sp) { + if (allow_relocation) { + asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), + Z_thread, + "SP was not set, still zero", + 0x202); + } else { + asm_assert_mem8_isnot_zero_static(in_bytes(JavaThread::last_Java_sp_offset()), + Z_thread, + "SP was not set, still zero", + 0x202); + } } // _last_Java_sp = 0 @@ -3835,15 +3846,14 @@ void MacroAssembler::reset_last_Java_frame(bool allow_relocation) { return; } -void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation) { +void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation, Label* jpc) { assert_different_registers(sp, tmp1); - // We cannot trust that code generated by the C++ compiler saves R14 - // to z_abi_160.return_pc, because sometimes it spills R14 using stmg at - // z_abi_160.gpr14 (e.g. InterpreterRuntime::_new()). - // Therefore we load the PC into tmp1 and let set_last_Java_frame() save - // it into the frame anchor. - get_PC(tmp1); + if (jpc == nullptr || jpc->is_bound()) { + load_const_optimized(tmp1, jpc == nullptr ? pc() : target(*jpc)); + } else { + load_const(tmp1, *jpc); + } set_last_Java_frame(/*sp=*/sp, /*pc=*/tmp1, allow_relocation); } @@ -5889,7 +5899,7 @@ bool is_excluded(Register excluded_register[], Register reg, int n) { } void MacroAssembler::clobber_volatile_registers(Register excluded_register[], int n) { - const int magic_number = 0x82; + const int magic_number = 0xbadbad; for (int i = 0; i < 6 /* R0 to R5 */; i++) { Register reg = as_Register(i); @@ -5898,6 +5908,26 @@ void MacroAssembler::clobber_volatile_registers(Register excluded_register[], in } } } + +void MacroAssembler::clobber_nonvolatile_registers() { + BLOCK_COMMENT("clobber_nonvolatile_registers {"); + static const Register regs[] = { + Z_R6, + Z_R7, + // don't zap Z_thread (Z_R8) + Z_R9, + Z_R10, + Z_R11, + Z_R12, + Z_R13 + }; + Register bad = regs[0]; + load_const_optimized(bad, 0xbad0101babe11111); + for (uint32_t i = 1; i < (sizeof(regs) / sizeof(Register)); i++) { + z_lgr(regs[i], bad); + } + BLOCK_COMMENT("} clobber_nonvolatile_registers"); +} #endif // ASSERT // Save and restore functions: Exclude Z_R0. @@ -6148,7 +6178,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register temp1 if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(temp1, obj); z_tm(Address(temp1, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - z_brne(slow); + z_brnaz(slow); } // First we need to check if the lock-stack has room for pushing the object reference. @@ -6741,6 +6771,39 @@ void MacroAssembler::pop_count_int_with_ext3(Register r_dst, Register r_src) { BLOCK_COMMENT("} pop_count_int_with_ext3"); } +void MacroAssembler::post_call_nop() { + // Make inline again when loom is always enabled. + if (!Continuations::enabled()) { + return; + } + nop(); + // TODO: + // 1. https://bugs.openjdk.org/browse/JDK-8300002 + // 2. https://bugs.openjdk.org/browse/JDK-8290965 +} + +void MacroAssembler::push_cont_fastpath() { + BLOCK_COMMENT("push_cont_fastpath {"); + if (!Continuations::enabled()) return; + NearLabel done; + z_clg(Z_SP, Address(Z_thread, JavaThread::cont_fastpath_offset())); + z_brnh(done); // bcondNotHigh -> less than equal + z_stg(Z_SP, Address(Z_thread, JavaThread::cont_fastpath_offset())); + bind(done); + BLOCK_COMMENT("} push_cont_fastpath"); +} + +void MacroAssembler::pop_cont_fastpath() { + BLOCK_COMMENT("pop_cont_fastpath {"); + if (!Continuations::enabled()) return; + NearLabel done; + z_clg(Z_SP, Address(Z_thread, JavaThread::cont_fastpath_offset())); + z_brl(done); + z_mvghi(Address(Z_thread, JavaThread::cont_fastpath_offset()), 0); + bind(done); + BLOCK_COMMENT("} pop_cont_fastpath"); +} + // LOAD HALFWORD IMMEDIATE ON CONDITION (32 <- 16) void MacroAssembler::load_on_condition_imm_32(Register dst, int64_t i2, branch_condition cc) { if (VM_Version::has_LoadStoreConditional2()) { // z_lochi works on z13 or above @@ -6766,3 +6829,156 @@ void MacroAssembler::load_on_condition_imm_64(Register dst, int64_t i2, branch_c bind(done); } } + +// Handle the receiver type profile update given the "recv" klass. +// +// Normally updates the ReceiverData (RD) that starts at "mdp" + "mdp_offset". +// If there are no matching or claimable receiver entries in RD, updates +// the polymorphic counter. +// +// This code expected to run by either the interpreter or JIT-ed code, without +// extra synchronization. For safety, receiver cells are claimed atomically, which +// avoids grossly misrepresenting the profiles under concurrent updates. For speed, +// counter updates are not atomic. +// +void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_offset, Register scratch) { + Register r0_tmp = Z_R0_scratch; // cannot be used in address calculation + assert_different_registers(recv, mdp, scratch, r0_tmp); + + int base_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(0)); + int end_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(ReceiverTypeData::row_limit())); + int poly_count_offset = in_bytes(CounterData::count_offset()); + int receiver_step = in_bytes(ReceiverTypeData::receiver_offset(1)) - base_receiver_offset; + int receiver_to_count_step = in_bytes(ReceiverTypeData::receiver_count_offset(0)) - base_receiver_offset; + + // Adjust for MDP offsets. + base_receiver_offset += mdp_offset; + end_receiver_offset += mdp_offset; + poly_count_offset += mdp_offset; + +#ifdef ASSERT + // We are about to walk the MDO slots without asking for offsets. + // Check that our math hits all the right spots. + for (uint c = 0; c < ReceiverTypeData::row_limit(); c++) { + int real_recv_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_offset(c)); + int real_count_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_count_offset(c)); + int offset = base_receiver_offset + receiver_step*c; + int count_offset = offset + receiver_to_count_step; + assert(offset == real_recv_offset, "receiver slot math"); + assert(count_offset == real_count_offset, "receiver count math"); + } + int real_poly_count_offset = mdp_offset + in_bytes(CounterData::count_offset()); + assert(poly_count_offset == real_poly_count_offset, "poly counter math"); +#endif + + // Corner case: no profile table. Increment poly counter and exit. + if (ReceiverTypeData::row_limit() == 0) { + add2mem_64(Address(mdp, poly_count_offset), DataLayout::counter_increment, scratch); + return; + } + + NearLabel L_loop_search_receiver, L_loop_search_empty; + NearLabel L_restart, L_found_recv, L_found_empty, L_count_update; + Register offset = scratch; + + // The code here recognizes three major cases: + // A. Fastest: receiver found in the table + // B. Fast: no receiver in the table, and the table is full + // C. Slow: no receiver in the table, free slots in the table + // + // The case A performance is most important, as perfectly-behaved code would end up + // there, especially with larger TypeProfileWidth. The case B performance is + // important as well, this is where bulk of code would land for normally megamorphic + // cases. The case C performance is not essential, its job is to deal with installation + // races, we optimize for code density instead. Case C needs to make sure that receiver + // rows are only claimed once. This makes sure we never overwrite a row for another + // receiver and never duplicate the receivers in the list, making profile type-accurate. + // + // It is very tempting to handle these cases in a single loop, and claim the first slot + // without checking the rest of the table. But, profiling code should tolerate free slots + // in the table, as class unloading can clear them. After such cleanup, the receiver + // we need might be _after_ the free slot. Therefore, we need to let at least full scan + // to complete, before trying to install new slots. Splitting the code in several tight + // loops also helpfully optimizes for cases A and B. + // + // This code is effectively: + // + // restart: + // // Fastest: receiver is already installed + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == recv) goto found_recv(i); + // } + // + // // Fast: no receiver, but profile is not full + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == null) goto found_null(i); + // } + // goto polymorphic + // + // // Slow: try to install receiver + // found_null(i): + // CAS(&receiver(i), null, recv); + // goto restart + // + // polymorphic: + // count++; + // return + // + // found_recv(i): + // *receiver_count(i)++ + // + + bind(L_restart); + + // Fastest: receiver is already installed + load_const_optimized(offset, base_receiver_offset); + + bind(L_loop_search_receiver); + z_cg(recv, Address(mdp, offset)); + z_bre(L_found_recv); + add2reg(offset, receiver_step); + compare64_and_branch(offset, end_receiver_offset, bcondNotEqual, L_loop_search_receiver); + + // Fast: no receiver, but profile is not full + load_const_optimized(offset, base_receiver_offset); + + bind(L_loop_search_empty); + z_ltg(r0_tmp, Address(mdp, offset)); + z_brz(L_found_empty); + add2reg(offset, receiver_step); + compare64_and_branch(offset, end_receiver_offset, bcondNotEqual, L_loop_search_empty); + + // Slow: Receiver is not found and table is full. + // Increment polymorphic counter instead of receiver slot. + load_const_optimized(offset, poly_count_offset); + z_bru(L_count_update); + + // Slowest: try to install receiver + bind(L_found_empty); + + { + // Atomically swing receiver slot: null -> recv. + // Use compare-and-swap to claim the slot. + Register receiver_addr = offset; + z_agr(receiver_addr, mdp); // receiver_addr = mdp + offset + + // r0_tmp is used as expected value (0), recv is the new value + z_lghi(r0_tmp, 0); + z_csg(r0_tmp, recv, 0, receiver_addr); + } + + // CAS success means the slot now has the receiver we want. CAS failure means + // something had claimed the slot concurrently: it can be the same receiver we want, + // or something else. Since this is a slow path, we can optimize for code density, + // and just restart the search from the beginning. + z_bru(L_restart); + + // Found a receiver, convert its slot offset to corresponding count offset. + bind(L_found_recv); + add2reg(offset, receiver_to_count_step); + + // Finally, update the counter + bind(L_count_update); + z_agr(offset, mdp); + add2mem_64(Address(offset), DataLayout::counter_increment, r0_tmp); +} diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 34389917cef..9dd4054a36c 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -1,7 +1,7 @@ /* * 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. + * Copyright (c) 2024, 2026, IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -525,12 +525,13 @@ class MacroAssembler: public Assembler { Register last_java_sp, // To set up last_Java_frame in stubs; use noreg otherwise. address entry_point, // The entry point. bool allow_relocation, // Flag to request generation of relocatable code. - bool check_exception); // Flag which indicates if exception should be checked. + bool check_exception, // Flag which indicates if exception should be checked. + Label *last_java_pc); // Call into the VM. // Passes the thread pointer (in Z_ARG1) as a prepended argument. // Makes sure oop return values are visible to the GC. - void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true, Label* last_java_pc = nullptr); void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, @@ -575,6 +576,8 @@ class MacroAssembler: public Assembler { // Get the pc where the last call will return to. Returns _last_calls_return_pc. inline address last_calls_return_pc(); + void post_call_nop(); + static int ic_check_size(); int ic_check(int end_alignment); @@ -805,14 +808,14 @@ class MacroAssembler: public Assembler { // Support for last Java frame (but use call_VM instead where possible). private: void set_last_Java_frame(Register last_Java_sp, Register last_Java_pc, bool allow_relocation); - void reset_last_Java_frame(bool allow_relocation); - void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation); + void reset_last_Java_frame(bool check_last_java_sp, bool allow_relocation); + void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation, Label* last_java_pc = nullptr); public: inline void set_last_Java_frame(Register last_java_sp, Register last_Java_pc); inline void set_last_Java_frame_static(Register last_java_sp, Register last_Java_pc); - inline void reset_last_Java_frame(void); - inline void reset_last_Java_frame_static(void); - inline void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1); + inline void reset_last_Java_frame(bool check_last_java_sp = true); + inline void reset_last_Java_frame_static(bool check_last_java_sp = true); + inline void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, Label* jpc = nullptr); inline void set_top_ijava_frame_at_SP_as_last_Java_frame_static(Register sp, Register tmp1); void set_thread_state(JavaThreadState new_state); @@ -979,6 +982,10 @@ class MacroAssembler: public Assembler { } void asm_assert_frame_size(Register expected_size, Register tmp, const char* msg, int id); + // Load bad values into registers that are nonvolatile according to the ABI except Z_thread. + // This is done after vthread preemption and before vthread resume. + void clobber_nonvolatile_registers() NOT_DEBUG_RETURN; + // Save and restore functions: Exclude Z_R0. void save_volatile_regs( Register dst, int offset, bool include_fp, bool include_flags); void restore_volatile_regs(Register src, int offset, bool include_fp, bool include_flags); @@ -1109,8 +1116,13 @@ class MacroAssembler: public Assembler { void pop_count_int_with_ext3(Register dst, Register src); void pop_count_long_with_ext3(Register dst, Register src); + void push_cont_fastpath(); + void pop_cont_fastpath(); + void load_on_condition_imm_32(Register dst, int64_t i2, branch_condition cc); void load_on_condition_imm_64(Register dst, int64_t i2, branch_condition cc); + + void profile_receiver_type(Register recv, Register mdp, int mdp_offset, Register tmp1); }; #ifdef ASSERT diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp index 72724fb66d1..24bec32f8b4 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.inline.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, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -295,16 +295,16 @@ inline void MacroAssembler::set_last_Java_frame_static(Register last_Java_sp, Re set_last_Java_frame(last_Java_sp, last_Java_pc, false); } -inline void MacroAssembler::reset_last_Java_frame(void) { - reset_last_Java_frame(true); +inline void MacroAssembler::reset_last_Java_frame(bool check_last_java_sp) { + reset_last_Java_frame(check_last_java_sp, true); } -inline void MacroAssembler::reset_last_Java_frame_static(void) { - reset_last_Java_frame(false); +inline void MacroAssembler::reset_last_Java_frame_static(bool check_last_java_sp) { + reset_last_Java_frame(check_last_java_sp, false); } -inline void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1) { - set_top_ijava_frame_at_SP_as_last_Java_frame(sp, tmp1, true); +inline void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, Label *jpc) { + set_top_ijava_frame_at_SP_as_last_Java_frame(sp, tmp1, true, jpc); } inline void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame_static(Register sp, Register tmp1) { diff --git a/src/hotspot/cpu/s390/nativeInst_s390.cpp b/src/hotspot/cpu/s390/nativeInst_s390.cpp index 546f8b13397..3520e9a3493 100644 --- a/src/hotspot/cpu/s390/nativeInst_s390.cpp +++ b/src/hotspot/cpu/s390/nativeInst_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 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -630,3 +630,32 @@ void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) *(intptr_t*)instr_addr = load_const_bytes | bytes_after_jump; ICache::invalidate_range(instr_addr, 6); } + +void NativeDeoptInstruction::verify() { +} + +void NativePostCallNop::make_deopt() { + NativeDeoptInstruction::insert(addr_at(0)); +} + +void NativeDeoptInstruction::insert(address code_pos) { + ResourceMark rm; + int code_size = 2; // z_illtrap is of 2 bytes + CodeBuffer cb(code_pos, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); + a->z_illtrap(); + // forcing CPU to reload these 2 bytes of instruction by setting current range invalid + ICache::invalidate_range(code_pos, code_size); +} + +bool NativeDeoptInstruction::is_deopt_at(address instr){ + // Check if the instruction is an illtrap (illegal instruction used for deoptimization) + if (!Assembler::is_z_illtrap(instr)) return false; + + // Verify the instruction belongs to an nmethod + CodeBlob* cb = CodeCache::find_blob(instr); + if (cb == nullptr || !cb->is_nmethod()) { + return false; + } + return true; +} diff --git a/src/hotspot/cpu/s390/nativeInst_s390.hpp b/src/hotspot/cpu/s390/nativeInst_s390.hpp index 9852bc410b1..0ba97830bb7 100644 --- a/src/hotspot/cpu/s390/nativeInst_s390.hpp +++ b/src/hotspot/cpu/s390/nativeInst_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. * @@ -82,6 +82,11 @@ class NativeInstruction { bool is_illegal(); + bool is_nop() const { + // TODO update: https://bugs.openjdk.org/browse/JDK-8290965 + return Assembler::is_z_nop(addr_at(0)); + } + // Bcrl is currently the only accepted instruction here. bool is_jump(); @@ -650,39 +655,40 @@ class NativeGeneralJump: public NativeInstruction { class NativePostCallNop: public NativeInstruction { public: enum z_specific_constants { - // Once the check is implemented, this has to specify number of bytes checked on the first - // read. If the check would read beyond size of the instruction at the deopt handler stub - // code entry point, then it has to happen in two stages - to prevent out of bounds access - // in case the return address points to the entry point which could be at the end of page. - first_check_size = 0 // check is unimplemented + // The check reads a 2-byte nop instruction. Since s390 nop is 2 bytes (BCR instruction), + // we can safely read it in a single stage without risk of out-of-bounds access. + // The nop instruction is checked by is_nop() which reads a short (2 bytes). + first_check_size = 2 }; - bool check() const { Unimplemented(); return false; } + bool check() const { return is_nop(); } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; } bool patch(int32_t oopmap_slot, int32_t cb_offset) { Unimplemented(); return false; } - void make_deopt() { Unimplemented(); } + void make_deopt(); }; inline NativePostCallNop* nativePostCallNop_at(address address) { - // Unimplemented(); + NativePostCallNop* nop = (NativePostCallNop*) address; + if (nop->check()) { + return nop; + } return nullptr; } class NativeDeoptInstruction: public NativeInstruction { public: - address instruction_address() const { Unimplemented(); return nullptr; } - address next_instruction_address() const { Unimplemented(); return nullptr; } + enum { + instruction_offset = 0 + }; - void verify() { Unimplemented(); } + address instruction_address() const { return addr_at(instruction_offset); } + address next_instruction_address() const { return instruction_address() + Assembler::instr_len(addr_at(0)); } - static bool is_deopt_at(address instr) { - // Unimplemented(); - return false; - } + void verify(); + + static bool is_deopt_at(address instr); // MT-safe patching - static void insert(address code_pos) { - Unimplemented(); - } + static void insert(address code_pos); }; #endif // CPU_S390_NATIVEINST_S390_HPP diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 2208a197ac9..5f33cf4fa77 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1929,23 +1929,23 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 15 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { return _Z_RARG4_INT_REG_mask; } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { return _Z_RARG3_INT_REG_mask; } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { return _Z_RARG4_LONG_REG_mask; } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { return _Z_RARG3_LONG_REG_mask; } @@ -2361,6 +2361,7 @@ encode %{ unsigned int actual_ret_off = __ offset(); assert(start_off + size_of_code == actual_ret_off, "wrong return_pc"); #endif + __ post_call_nop(); %} enc_class z_enc_java_static_call(method meth) %{ @@ -2393,6 +2394,7 @@ encode %{ } __ clear_inst_mark(); + __ post_call_nop(); %} // Java dynamic call @@ -2449,6 +2451,7 @@ encode %{ __ z_basr(Z_R14, Z_R1_scratch); unsigned int ret_off = __ offset(); } + __ post_call_nop(); %} enc_class z_enc_cmov_reg(cmpOp cmp, iRegI dst, iRegI src) %{ @@ -2614,28 +2617,31 @@ frame %{ // stack slot. return_addr(REG Z_R14); - // Location of native (C/C++) and interpreter return values. This - // is specified to be the same as Java. In the 32-bit VM, long - // values are actually returned from native calls in O0:O1 and - // returned to the interpreter in I0:I1. The copying to and from - // the register pairs is done by the appropriate call and epilog - // opcodes. This simplifies the register allocator. - // - // Use register pair for c return value. - c_return_value %{ - assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values"); - static int typeToRegLo[Op_RegL+1] = { 0, 0, Z_R2_num, Z_R2_num, Z_R2_num, Z_F0_num, Z_F0_num, Z_R2_num }; - static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, Z_R2_H_num, OptoReg::Bad, Z_F0_H_num, Z_R2_H_num }; - return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]); - %} - // Use register pair for return value. // Location of compiled Java return values. Same as C return_value %{ assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values"); - static int typeToRegLo[Op_RegL+1] = { 0, 0, Z_R2_num, Z_R2_num, Z_R2_num, Z_F0_num, Z_F0_num, Z_R2_num }; - static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, Z_R2_H_num, OptoReg::Bad, Z_F0_H_num, Z_R2_H_num }; - return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]); + static const int lo[Op_RegL + 1] = { + 0, + 0, + Z_R2_num, // Op_RegN + Z_R2_num, // Op_RegI + Z_R2_num, // Op_RegP + Z_F0_num, // Op_RegF + Z_F0_num, // Op_RegD + Z_R2_num // Op_RegL + }; + static const int hi[Op_RegL + 1] = { + 0, + 0, + OptoReg::Bad, // Op_RegN + OptoReg::Bad, // Op_RegI + Z_R2_H_num, // Op_RegP + OptoReg::Bad, // Op_RegF + Z_F0_H_num, // Op_RegD + Z_R2_H_num // Op_RegL + }; + return OptoRegPair(hi[ideal_reg], lo[ideal_reg]); %} %} @@ -5557,6 +5563,38 @@ instruct compareAndSwapN_bool(iRegP mem_ptr, rarg5RegN oldval, iRegN_P2N newval, ins_pipe(pipe_class_dummy); %} +instruct compareAndExchangeN(iRegN res, iRegP mem_ptr, rarg5RegN oldval, iRegN_P2N newval, flagsReg cr) %{ + match(Set res (CompareAndExchangeN mem_ptr (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); + effect(TEMP_DEF res, USE mem_ptr, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndExchangeN $oldval,$newval,$mem_ptr" %} + ins_encode %{ + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + Register Rres = reg_to_register_object($res$$reg); + __ z_lr(Rres, Rcomp); + __ z_cs(Rres, Rnew, 0, Raddr); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct compareAndExchangeP(iRegP res, iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval, flagsReg cr) %{ + match(Set res (CompareAndExchangeP mem_ptr (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); + effect(TEMP_DEF res, USE mem_ptr, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndExchangeP $oldval,$newval,$mem_ptr" %} + ins_encode %{ + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + Register Rres = reg_to_register_object($res$$reg); + __ z_lgr(Rres, Rcomp); + __ z_csg(Rres, Rnew, 0, Raddr); + %} + ins_pipe(pipe_class_dummy); +%} + //----------Atomic operations on memory (GetAndSet*, GetAndAdd*)--------------- // Exploit: direct memory arithmetic @@ -8748,6 +8786,7 @@ instruct compP_decode_reg_imm0(flagsReg cr, iRegN op1, immP0 op2) %{ instruct compP_reg_mem(iRegP dst, memory src, flagsReg cr)%{ match(Set cr (CmpP dst (LoadP src))); + predicate(n->in(2)->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "CLG $dst, $src\t # ptr" %} diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index e5a27e66968..1a13e76e930 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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 +38,8 @@ #include "oops/klass.inline.hpp" #include "prims/methodHandles.hpp" #include "registerSaver_s390.hpp" +#include "runtime/continuation.hpp" +#include "runtime/continuationEntry.inline.hpp" #include "runtime/jniHandles.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" @@ -1339,6 +1342,395 @@ static void move32_64(MacroAssembler *masm, // Wrap a JNI call. //---------------------------------------------------------------------- #undef USE_RESIZE_FRAME + +static void check_continuation_enter_argument(VMReg actual_vmreg, + Register expected_reg, + const char* name) { + assert(!actual_vmreg->is_stack(), "%s cannot be on stack", name); + assert(actual_vmreg->as_Register() == expected_reg, + "%s is in unexpected register: %s instead of %s", + name, actual_vmreg->as_Register()->name(), expected_reg->name()); +} + +//---------------------------- continuation_enter_setup --------------------------- +// +// Frame setup. +// +// Arguments: +// None. +// +// Results: +// Z_SP: pointer to blank ContinuationEntry in the pushed frame. +// +// Kills: +// Nothing +// +static OopMap* continuation_enter_setup(MacroAssembler* masm, int& framesize_words) { + + assert(ContinuationEntry::size() % VMRegImpl::stack_slot_size == 0, ""); + assert(in_bytes(ContinuationEntry::cont_offset()) % VMRegImpl::stack_slot_size == 0, ""); + assert(in_bytes(ContinuationEntry::chunk_offset()) % VMRegImpl::stack_slot_size == 0, ""); + + const int frame_size_in_bytes = (int)ContinuationEntry::size(); + assert(is_aligned(frame_size_in_bytes, frame::alignment_in_bytes), "alignment error"); + + framesize_words = frame_size_in_bytes / wordSize; + + DEBUG_ONLY(__ block_comment("continuation_enter_setup {")); + __ save_return_pc(); // preserve current Z_R14 + __ push_frame(frame_size_in_bytes); + + OopMap* map = new OopMap((int)frame_size_in_bytes / VMRegImpl::stack_slot_size, 0 /* arg_slots*/); + __ z_mvc(Address(Z_SP, ContinuationEntry::parent_offset()), /* move to */ + Address(Z_thread, JavaThread::cont_entry_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + __ z_stg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + DEBUG_ONLY(__ block_comment("} continuation_enter_setup")); + return map; +} + +//---------------------------- fill_continuation_entry --------------------------- +// +// Initialize the new ContinuationEntry. +// +// Arguments: +// Z_SP : pointer to blank Continuation entry +// reg_cont_obj : pointer to the continuation +// reg_flags : flags / isVirtualThread +// +// Results: +// Z_SP : pointer to filled out ContinuationEntry +// +// Kills: +// This is peace driven method, doesn't kill anyone. +// +static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, Register reg_flags) { + assert_different_registers(reg_cont_obj, reg_flags); + DEBUG_ONLY(__ block_comment("fill_continuation_entry {")); +#ifdef ASSERT + assert(Immediate::is_simm16(ContinuationEntry::cookie_value()), "update below instruction"); + __ z_mvhi(Address(Z_SP, ContinuationEntry::cookie_offset()), ContinuationEntry::cookie_value()); +#endif //ASSERT + __ z_stg(reg_cont_obj, Address(Z_SP, ContinuationEntry::cont_offset())); + __ z_st(reg_flags, Address(Z_SP, ContinuationEntry::flags_offset())); + __ z_mvghi(Address(Z_SP, ContinuationEntry::chunk_offset()), 0); + __ z_mvhi( Address(Z_SP, ContinuationEntry::argsize_offset()), 0); + __ z_mvhi( Address(Z_SP, ContinuationEntry::pin_count_offset()), 0); + + __ z_mvc(Address(Z_SP, ContinuationEntry::parent_cont_fastpath_offset()), /* move to */ + Address(Z_thread, JavaThread::cont_fastpath_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + + __ z_mvghi(Address(Z_thread, JavaThread::cont_fastpath_offset()), 0); + + DEBUG_ONLY(__ block_comment("} fill_continuation_entry")); +} + +//---------------------------- continuation_enter_cleanup --------------------------- +// +// Copy corresponding attributes from the top ContinuationEntry to the JavaThread +// before deleting it. +// +// Arguments: +// Z_SP: pointer to the ContinuationEntry +// +// Results: +// None. +// +// Kills: +// Z_R0_scratch (in debug builds) +// Z_R10 (when CheckJNICalls is enabled) +// +static void continuation_enter_cleanup(MacroAssembler* masm) { + __ block_comment("continuation_enter_cleanup {"); + +#ifdef ASSERT + __ z_cg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + __ asm_assert(Assembler::bcondEqual, FILE_AND_LINE ": incorrect Z_SP", 0x1bb); + + __ z_lgf(Z_R0, Address(Z_SP, ContinuationEntry::cookie_offset())); + __ z_cfi(Z_R0, ContinuationEntry::cookie_value()); + __ asm_assert(Assembler::bcondEqual, FILE_AND_LINE ": incorrect cookie value", 0x1cc); +#endif // ASSERT + + __ z_mvc(Address(Z_thread, JavaThread::cont_fastpath_offset()), /* move to */ + Address(Z_SP, ContinuationEntry::parent_cont_fastpath_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + + __ z_mvc(Address(Z_thread, JavaThread::cont_entry_offset()), /* move to */ + Address(Z_SP, ContinuationEntry::parent_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + + __ block_comment("} continuation_enter_cleanup"); +} +static void gen_continuation_enter(MacroAssembler* masm, + const VMRegPair* regs, + int& exception_offset, + OopMapSet* oop_maps, + int& frame_complete, + int& framesize_words, + int& interpreted_entry_offset, + int& compiled_entry_offset) { + // enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread) + int pos_cont_obj = 0; + int pos_is_cont = 1; + int pos_is_virtual = 2; + + // The platform-specific calling convention may present the arguments in various registers. + // To simplify the rest of the code, we expect the arguments to reside at these known + // registers, and we additionally check the placement here in case calling convention ever + // changes. + Register reg_cont_obj = Z_ARG1; + Register reg_is_cont = Z_ARG2; + Register reg_is_virtual = Z_ARG3; + + check_continuation_enter_argument(regs[pos_cont_obj].first(), reg_cont_obj, "Continuation object"); + check_continuation_enter_argument(regs[pos_is_cont].first(), reg_is_cont, "isContinue"); + check_continuation_enter_argument(regs[pos_is_virtual].first(), reg_is_virtual, "isVirtualThread"); + + address resolve_static_call = SharedRuntime::get_resolve_static_call_stub(); + + address start = __ pc(); + + Label L_thaw, L_exit; + + // i2i entry used at interp_only_mode only + interpreted_entry_offset = __ pc() - start; + { +#ifdef ASSERT + NearLabel is_interp_only; + __ load_and_test_int(Z_R0_scratch, Address(Z_thread, JavaThread::interp_only_mode_offset())); + __ z_brnz(is_interp_only); + __ stop("enterSpecial interpreter entry called when not in interp_only_mode"); + __ bind(is_interp_only); +#endif + + // Read interpreter arguments into registers (this is an ad-hoc i2c adapter) + // s390x stores frame pointer in the slot 0, so argument will be loaded from slot 1 + __ z_lg(reg_cont_obj, Address(Z_esp, Interpreter::stackElementSize*3)); + __ z_llgf(reg_is_cont, Address(Z_esp, Interpreter::stackElementSize*2)); + __ z_llgf(reg_is_virtual, Address(Z_esp, Interpreter::stackElementSize*1)); + + __ push_cont_fastpath(); + + OopMap* map = continuation_enter_setup(masm, framesize_words); + + // The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe, + // but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway. + + __ verify_oop(reg_cont_obj); + + fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); + + // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) + __ compare32_and_branch(reg_is_cont, 0, Assembler::bcondNotZero, L_thaw); + + // --- call Continuation.enter(Continuation c, boolean isContinue) + + // Emit compiled static call. The call will be always resolved to the c2i + // entry of Continuation.enter(Continuation c, boolean isContinue). + // There are special cases in SharedRuntime::resolve_static_call_C() and + // SharedRuntime::resolve_sub_helper_internal() to achieve this + // See also corresponding call below. + // Make sure the call is patchable + + __ align(NativeCall::call_far_pcrelative_displacement_alignment, + __ offset() + NativeCall::call_far_pcrelative_displacement_offset); + + // Emit stub for static call + address stub = CompiledDirectCall::emit_to_interp_stub(masm, __ pc()); + if (stub == nullptr) { + fatal("CodeCache is full at gen_continuation_enter"); + } + __ relocate(relocInfo::static_call_type); + __ z_nop(); + __ z_brasl(Z_R14, resolve_static_call); + oop_maps->add_gc_map(__ pc() - start, map); + __ post_call_nop(); + __ branch_optimized(Assembler::bcondAlways, L_exit); + } + + // compiled entry + __ align(CodeEntryAlignment); + compiled_entry_offset = __ pc() - start; + + OopMap* map = continuation_enter_setup(masm, framesize_words); + + // Frame is now completed as far as size and linkage. + + frame_complete =__ pc() - start; + + __ verify_oop(reg_cont_obj); + + fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); + + // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) + __ z_ltr(reg_is_cont, reg_is_cont); + __ branch_optimized(Assembler::bcondNotEqual, L_thaw); // was reg_is_cont equal to 0 ? + + // --- call Continuation.enter(Continuation c, boolean isContinue) + + // Make sure the call is patchable + __ align(NativeCall::call_far_pcrelative_displacement_alignment, + __ offset() + NativeCall::call_far_pcrelative_displacement_offset); + + // Emit stub for static call + address stub = CompiledDirectCall::emit_to_interp_stub(masm, __ pc()); + guarantee(stub != nullptr, "CodeCache is full at gen_continuation_enter"); + + assert((__ offset() + NativeCall::call_far_pcrelative_displacement_offset) % NativeCall::call_far_pcrelative_displacement_alignment == 0, + "must be aligned (offset=%d)", __ offset()); + + // The call needs to be resolved. There's a special case for this in + // SharedRuntime::find_callee_info_helper() which calls + // LinkResolver::resolve_continuation_enter() which resolves the call to + // Continuation.enter(Continuation c, boolean isContinue). + __ relocate(relocInfo::static_call_type); + __ z_nop(); + __ z_brasl(Z_R14, resolve_static_call); + oop_maps->add_gc_map(__ pc() - start, map); + __ post_call_nop(); + + __ branch_optimized(Assembler::bcondAlways, L_exit); + + // --- Thawing path + + __ bind(L_thaw); + ContinuationEntry::_thaw_call_pc_offset = __ pc() - start; + __ load_const_optimized(Z_R1_scratch, StubRoutines::cont_thaw()); + __ call(Z_R1_scratch); + oop_maps->add_gc_map(__ pc() - start, map->deep_copy()); + ContinuationEntry::_return_pc_offset = __ pc() - start; + __ post_call_nop(); + + // --- Normal exit (resolve/thawing) + __ bind(L_exit); + ContinuationEntry::_cleanup_offset = __ pc() - start; + continuation_enter_cleanup(masm); + + // Pop frame and return + DEBUG_ONLY(__ z_lg(Z_R0, Address(Z_SP, 0))); + __ add2reg(Z_SP, framesize_words * wordSize); + +#ifdef ASSERT + NearLabel ok; + __ z_cgr(Z_R0, Z_SP); + __ z_bre(ok); + __ stop("inconsistent frame size"); + __ bind(ok); +#endif // ASSERT + + __ restore_return_pc(); + __ z_br(Z_R14); + + // --- Exception handling path + exception_offset = __ pc() - start; + + continuation_enter_cleanup(masm); + + // Load caller's return pc + __ z_lg(Z_ARG2, _z_common_abi(callers_sp), Z_SP); + __ z_lg(Z_ARG2, _z_common_abi(return_pc), Z_ARG2); + + __ save_return_pc(); + __ push_frame_abi160(0 + 2 * BytesPerWord); + + __ z_stg(Z_ARG1, 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save return value containing the exception oop + __ z_stg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save exception_pc + + // Find exception handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + Z_thread, + Z_ARG2); + + // Copy handler's address. + __ z_lgr(Z_R1, Z_RET); + + // Set up the arguments for the exception handler: + // - Z_ARG1: exception oop + // - Z_ARG2: exception pc + __ z_lg(Z_ARG1, 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception oop + __ z_lg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception pc + + __ pop_frame(); // pop frame pushed before runtime call + // __ restore_return_pc(); // can be skipped + + __ pop_frame(); // pop enterSpecial frame + __ restore_return_pc(); + + // Jump to exception handler + __ z_br(Z_R1 /*handler address*/); +} + +static void gen_continuation_yield(MacroAssembler* masm, + const VMRegPair* regs, + OopMapSet* oop_maps, + int& frame_complete, + int& framesize_words, + int& compiled_entry_offset) { + const int framesize_bytes = (int)align_up((int)frame::z_abi_160_size, frame::alignment_in_bytes); + framesize_words = framesize_bytes / wordSize; + + Register Rtmp = Z_R1_scratch; + + address start = __ pc(); + compiled_entry_offset = __ pc() - start; + + // Save return pc and push entry frame + __ save_return_pc(); + __ push_frame(framesize_bytes); + + DEBUG_ONLY(__ block_comment("Frame Complete (gen_continuation_yield):")); + frame_complete = __ pc() - start; + address last_java_pc = __ pc(); + + + // This nop must be exactly at the PC we push into the frame info. + // We use this nop for fast CodeBlob lookup, associate the OopMap + // with it right away. + __ post_call_nop(); + OopMap* map = new OopMap(framesize_bytes / VMRegImpl::stack_slot_size, 1); + oop_maps->add_gc_map(last_java_pc - start, map); + + __ z_larl(Rtmp, last_java_pc); + __ set_last_Java_frame(Z_SP, Rtmp); + __ call_VM_leaf(Continuation::freeze_entry(), Z_thread, Z_SP); + __ reset_last_Java_frame(); + + NearLabel L_pinned; + __ z_cij(Z_RET, 0, Assembler::bcondNotEqual, L_pinned); + + // Pop frames of continuation including this stub's frame + __ z_lg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + // The frame pushed by gen_continuation_enter() is on top now again + continuation_enter_cleanup(masm); + // Pop frame and return + Label L_return; + __ bind(L_return); + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + // yield failed - continuation is pinned + __ bind(L_pinned); + + // handle pending exception thrown by freeze + __ load_and_test_long(Rtmp, Address(Z_thread, Thread::pending_exception_offset())); + __ z_bre(L_return); // return if no exception is pending + __ pop_frame(); + __ restore_return_pc(); + __ load_const_optimized(Z_R1_scratch, StubRoutines::forward_exception_entry()); + __ z_br(Z_R1_scratch); +} + +void SharedRuntime::continuation_enter_cleanup(MacroAssembler* masm) { + ::continuation_enter_cleanup(masm); +} + nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, const methodHandle& method, int compile_id, @@ -1346,6 +1738,66 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, VMRegPair *in_regs, BasicType ret_type) { int total_in_args = method->size_of_parameters(); + if (method->is_continuation_native_intrinsic()) { + int exception_offset = -1; + OopMapSet* oop_maps = new OopMapSet(); + int frame_complete = -1; + int stack_slots = -1; + int interpreted_entry_offset = -1; + int vep_offset = -1; // verified entry point offset + if (method->is_continuation_enter_intrinsic()) { + gen_continuation_enter(masm, + in_regs, + exception_offset, + oop_maps, + frame_complete, + stack_slots, + interpreted_entry_offset, + vep_offset); + } else if(method->is_continuation_yield_intrinsic()) { + gen_continuation_yield(masm, + in_regs, + oop_maps, + frame_complete, + stack_slots, + vep_offset); + } else { + guarantee(false, "Unknown Continuation native intrinsic"); + } + +#ifdef ASSERT + if (method->is_continuation_enter_intrinsic()) { + assert(interpreted_entry_offset != -1, "Must be set"); + assert(exception_offset != -1, "Must be set"); + } else { + assert(interpreted_entry_offset == -1, "Must be unset"); + assert(exception_offset == -1, "Must be unset"); + } + assert(frame_complete != -1, "Must be set"); + assert(stack_slots != -1, "Must be set"); + assert(vep_offset != -1, "Must be set"); +#endif + + __ flush(); + nmethod* nm = nmethod::new_native_nmethod(method, + compile_id, + masm->code(), + vep_offset, + frame_complete, + stack_slots, + in_ByteSize(-1), + in_ByteSize(-1), + oop_maps, + exception_offset); + if (nm == nullptr) return nm; + if (method->is_continuation_enter_intrinsic()) { + ContinuationEntry::set_enter_code(nm, interpreted_entry_offset); + } else if (method->is_continuation_yield_intrinsic()) { + _cont_doYield_stub = nm; + } + return nm; + } + if (method->is_method_handle_intrinsic()) { vmIntrinsics::ID iid = method->intrinsic_id(); intptr_t start = (intptr_t) __ pc(); @@ -1545,6 +1997,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, unsigned int wrapper_FrameDone; unsigned int wrapper_CRegsSet; Label handle_pending_exception; + Label last_java_pc; //--------------------------------------------------------------------- // Unverified entry point (UEP) @@ -1726,16 +2179,9 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // So if we must call out we must push a new frame. ////////////////////////////////////////////////////////////////////// - - // Calc the current pc into Z_R10 and into wrapper_CRegsSet. - // Both values represent the same position. - __ get_PC(Z_R10); // PC into register - wrapper_CRegsSet = __ offset(); // and into into variable. - - // Z_R10 now has the pc loaded that we will use when we finally call to native. - - // We use the same pc/oopMap repeatedly when we call out. - oop_maps->add_gc_map((int)(wrapper_CRegsSet-wrapper_CodeStart), map); + // The last java pc will also be used as resume pc if this is the wrapper for wait0. + // For this purpose the precise location matters but not for oopmap lookup. + __ z_larl(Z_R10, last_java_pc); // Lock a synchronized method. @@ -1780,10 +2226,13 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ z_lgr(Z_ARG3, Z_thread); __ set_last_Java_frame(oldSP, Z_R10 /* gc map pc */); + assert(Z_R10->is_nonvolatile(), "Z_R10 needs to be preserved accross complete_monitor_locking_C call"); // Do the call. + __ push_cont_fastpath(); __ load_const_optimized(Z_R1_scratch, CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C)); __ call(Z_R1_scratch); + __ pop_cont_fastpath(); __ reset_last_Java_frame(); @@ -1910,6 +2359,23 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Transition from _thread_in_native_trans to _thread_in_Java. __ set_thread_state(_thread_in_Java); + // Check preemption for Object.wait() + if (method->is_object_wait0()) { + NearLabel not_preempted; + __ z_ltg(Z_R1_scratch, Address(Z_thread, JavaThread::preempt_alternate_return_offset())); + __ z_brz(not_preempted); // if 0, jump to not_preempted + __ z_mvghi(Address(Z_thread, JavaThread::preempt_alternate_return_offset()), 0); + __ z_br(Z_R1_scratch); + __ bind(not_preempted); + } + __ bind(last_java_pc); + + // Calc the current pc into wrapper_CRegsSet. + wrapper_CRegsSet = __ offset(); // and into into variable. + + // We use the same pc/oopMap repeatedly when we call out. + oop_maps->add_gc_map((int)(wrapper_CRegsSet-wrapper_CodeStart), map); + //-------------------------------------------------------------------- // Reguard any pages if necessary. // Protect native result from being destroyed. @@ -2012,7 +2478,10 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Clear "last Java frame" SP and PC. //-------------------------------------------------------------------- - __ reset_last_Java_frame(); + + // Last java frame won't be set if we're resuming after preemption + bool maybe_preempted = method->is_object_wait0(); + __ reset_last_Java_frame(/* check_last_java_sp = */ !maybe_preempted); // Unpack oop result, e.g. JNIHandles::resolve result. if (is_reference_type(ret_type)) { @@ -2317,6 +2786,8 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, } } + __ push_cont_fastpath(); // Set JavaThread::_cont_fastpath to the sp of the oldest interpreted frame we know about + // Jump to the compiled code just as if compiled code was doing it. // load target address from method: __ z_lg(Z_R1_scratch, Address(Z_method, Method::from_compiled_offset())); @@ -2416,8 +2887,7 @@ uint SharedRuntime::out_preserve_stack_slots() { } VMReg SharedRuntime::thread_register() { - Unimplemented(); - return nullptr; + return Z_thread->as_VMReg(); } // @@ -2678,6 +3148,13 @@ void SharedRuntime::generate_deopt_blob() { // stack: (caller_of_deoptee, ...). + // Freezing continuation frames requires that the caller is trimmed to unextended sp if compiled. + // If not compiled the loaded value is equal to the current SP (see frame::initial_deoptimization_info()) + // and the frame is effectively not resized. + Register caller_sp = Z_R1_scratch; + __ z_lg(caller_sp, Address(unroll_block_reg, Deoptimization::UnrollBlock::initial_info_offset())); + __ resize_frame_absolute(caller_sp, Z_R0, true); + // loop through the `UnrollBlock' info and create interpreter frames. push_skeleton_frames(masm, true/*deopt*/, unroll_block_reg, @@ -2809,6 +3286,13 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { __ zap_from_to(Z_SP, Z_SP, Z_R0_scratch, Z_R1, 500, -1); + // Freezing continuation frames requires that the caller is trimmed to unextended sp if compiled. + // If not compiled the loaded value is equal to the current SP (see frame::initial_deoptimization_info()) + // and the frame is effectively not resized. + Register caller_sp = Z_R1_scratch; + __ z_lg(caller_sp, Address(unroll_block_reg, Deoptimization::UnrollBlock::initial_info_offset())); + __ resize_frame_absolute(caller_sp, Z_R0, true); + // allocate new interpreter frame(s) and possibly resize the caller's frame // (no more adapters !) push_skeleton_frames(masm, false/*deopt*/, @@ -3387,16 +3871,76 @@ int SpinPause() { } #if INCLUDE_JFR + +// For c2: c_rarg0 is junk, call to runtime to write a checkpoint. +// It returns a jobject handle to the event writer. +// The handle is dereferenced and the return value is the event writer oop. RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + const char* name = SharedRuntime::stub_name(StubId::shared_jfr_write_checkpoint_id); + CodeBuffer code(name, 512, 64); + MacroAssembler* masm = new MacroAssembler(&code); + + int framesize = frame::z_abi_160_size / VMRegImpl::stack_slot_size; + address start = __ pc(); + __ save_return_pc(); // save return_pc (Z_R14) + __ push_frame_abi160(0); + int frame_complete = __ pc() - start; + __ set_last_Java_frame(Z_SP, noreg); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::write_checkpoint), Z_thread); + address calls_return_pc = __ last_calls_return_pc(); + __ reset_last_Java_frame(); + + // The handle is dereferenced through a load barrier. + __ resolve_global_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 0); + oop_maps->add_gc_map(calls_return_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub(name, &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + + return stub; } +// For c2: call to return a leased buffer. RuntimeStub* SharedRuntime::generate_jfr_return_lease() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + const char* name = SharedRuntime::stub_name(StubId::shared_jfr_return_lease_id); + CodeBuffer code(name, 512, 64); + MacroAssembler* masm = new MacroAssembler(&code); + + int framesize = frame::z_abi_160_size / VMRegImpl::stack_slot_size; + address start = __ pc(); + __ save_return_pc(); // save return_pc (Z_R14) + __ push_frame_abi160(0); + int frame_complete = __ pc() - start; + __ set_last_Java_frame(Z_SP, noreg); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), Z_thread); + address calls_return_pc = __ last_calls_return_pc(); + + __ reset_last_Java_frame(); + + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 0); + oop_maps->add_gc_map(calls_return_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub(name, &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + + return stub; } #endif // INCLUDE_JFR diff --git a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp index f338fb192ad..630a9516831 100644 --- a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp +++ b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp @@ -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 @@ -30,7 +30,7 @@ class SmallRegisterMap; -// Java frames don't have callee saved registers (except for rfp), so we can use a smaller SmallRegisterMapType +// Java frames don't have callee saved registers, so we can use a smaller RegisterMap template class SmallRegisterMapType { friend SmallRegisterMap; @@ -39,8 +39,6 @@ class SmallRegisterMapType { ~SmallRegisterMapType() = default; NONCOPYABLE(SmallRegisterMapType); - static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN - DEBUG_ONLY({ Unimplemented(); }) public: // as_RegisterMap is used when we didn't want to templatize and abstract over RegisterMap type to support SmallRegisterMap // Consider enhancing SmallRegisterMap to support those cases @@ -48,20 +46,21 @@ public: RegisterMap* as_RegisterMap() { return nullptr; } RegisterMap* copy_to_RegisterMap(RegisterMap* map, intptr_t* sp) const { - Unimplemented(); + map->clear(); + map->set_include_argument_oops(this->include_argument_oops()); return map; } inline address location(VMReg reg, intptr_t* sp) const { - Unimplemented(); + assert(false, "Reg: %s", reg->name()); return nullptr; } - inline void set_location(VMReg reg, address loc) { assert_is_rfp(reg); } + inline void set_location(VMReg reg, address loc) { assert(false, "Reg: %s", reg->name()); } JavaThread* thread() const { #ifndef ASSERT - guarantee (false, ""); + guarantee (false, "unreachable"); #endif return nullptr; } @@ -76,7 +75,7 @@ public: #ifdef ASSERT bool should_skip_missing() const { return false; } VMReg find_register_spilled_here(void* p, intptr_t* sp) { - Unimplemented(); + assert(false, "Shouldn't reach here! p:" PTR_FORMAT " sp:" PTR_FORMAT, p2i(p), p2i(p)); return nullptr; } void print() const { print_on(tty); } diff --git a/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp b/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp index e598117fe7d..3a5b860b7a7 100644 --- a/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp +++ b/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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,75 +33,120 @@ #ifdef ASSERT template inline bool StackChunkFrameStream::is_in_frame(void* p0) const { - Unimplemented(); - return true; + assert(!is_done(), ""); + assert(is_compiled(), ""); + intptr_t* p = (intptr_t*)p0; + int argsize = (_cb->as_nmethod()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord; + int frame_size = _cb->frame_size() + (argsize > 0 ? argsize + frame::metadata_words_at_top : 0); + return (p - unextended_sp()) >= 0 && (p - unextended_sp()) < frame_size; } #endif template inline frame StackChunkFrameStream::to_frame() const { - Unimplemented(); - return frame(); + if (is_done()) { + return frame(_sp, _sp, nullptr, nullptr, nullptr, nullptr, true); + } else { + // Compiled frames on heap don't have back links on s390. The back link is redundant + // and gets computed as unextended_sp + frame_size. In debug builds, FreezeBase::patch_pd() + // explicitly sets it to badAddress. + return frame(sp(), unextended_sp(), Interpreter::contains(pc()) ? fp() : nullptr, pc(), cb(), _oopmap, true); + } } template inline address StackChunkFrameStream::get_pc() const { - Unimplemented(); - return nullptr; + assert(!is_done(), ""); + return (address)((frame::z_common_abi*) _sp)->return_pc; } template inline intptr_t* StackChunkFrameStream::fp() const { - Unimplemented(); - return nullptr; + // See FreezeBase::patch_pd() and frame::setup() + assert((frame_kind == ChunkFrames::Mixed && is_interpreted()), ""); + intptr_t* fp_addr = (intptr_t*)&((frame::z_common_abi*)_sp)->callers_sp; + assert(*(intptr_t**)fp_addr != nullptr, ""); + // derelativize + return fp_addr + *fp_addr; } template inline intptr_t* StackChunkFrameStream::derelativize(int offset) const { - Unimplemented(); - return nullptr; + intptr_t* fp = this->fp(); + assert(fp != nullptr, ""); + return fp + fp[offset]; } template inline intptr_t* StackChunkFrameStream::unextended_sp_for_interpreter_frame() const { - Unimplemented(); - return nullptr; + assert_is_interpreted_and_frame_type_mixed(); + // Compute the unextended SP (stack pointer before any extension for arguments). + // On s390, esp points to the next free slot above the operand stack, so we add 1 + // to get the actual top of the operand stack, then subtract metadata_words to + // account for the frame metadata (callers_sp and return_pc) at the top of the frame. + return derelativize(_z_ijava_idx(esp)) + 1 - frame::metadata_words; } template inline void StackChunkFrameStream::next_for_interpreter_frame() { - Unimplemented(); + assert_is_interpreted_and_frame_type_mixed(); + if (derelativize(_z_ijava_idx(locals)) + 1 >= _end) { + _unextended_sp = _end; + _sp = _end; + } else { + _unextended_sp = derelativize(_z_ijava_idx(sender_sp)); + _sp = this->fp(); + } } template inline int StackChunkFrameStream::interpreter_frame_size() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + intptr_t* top = unextended_sp(); // later subtract argsize if callee is interpreted + intptr_t* bottom = derelativize(_z_ijava_idx(locals)) + 1; + return (int)(bottom - top); } +// Size of stack args in words (P0..Pn above). Only valid if the caller is also +// interpreted. The function is also called if the caller is compiled but the +// result is not used in that case (same on x86). +// See also setting of sender_sp in ContinuationHelper::InterpretedFrame::patch_sender_sp() template inline int StackChunkFrameStream::interpreter_frame_stack_argsize() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + frame::z_ijava_state* state = (frame::z_ijava_state*)((uintptr_t)fp() - frame::z_ijava_state_size); + int diff = (int)(state->locals - (state->sender_sp + frame::metadata_words_at_top) + 1); + assert(diff == -frame::metadata_words_at_top || ((Method*)state->method)->size_of_parameters() == diff, + "size_of_parameters(): %d diff: %d sp: " PTR_FORMAT " fp:" PTR_FORMAT, + ((Method*)state->method)->size_of_parameters(), diff, p2i(sp()), p2i(fp())); + return diff; } template template inline int StackChunkFrameStream::interpreter_frame_num_oops(RegisterMapT* map) const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + ResourceMark rm; + frame f = to_frame(); + InterpreterOopCount closure; + f.oops_interpreted_do(&closure, map); + return closure.count(); } template<> template<> inline void StackChunkFrameStream::update_reg_map_pd(RegisterMap* map) { - Unimplemented(); + // No register map update needed for s390. + // In the Java calling convention on s390, all registers are volatile (caller-saved), + // so there are no non-volatile (callee-saved) registers that need to be tracked. } template<> template<> inline void StackChunkFrameStream::update_reg_map_pd(RegisterMap* map) { - Unimplemented(); + // No register map update needed for s390. + // In the Java calling convention on s390, all registers are volatile (caller-saved), + // so there are no non-volatile (callee-saved) registers that need to be tracked. } template diff --git a/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp b/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp index dfd3562c9d9..c97751d0d1e 100644 --- a/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp +++ b/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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 +27,15 @@ #define CPU_S390_STACKCHUNKOOP_S390_INLINE_HPP inline void stackChunkOopDesc::relativize_frame_pd(frame& fr) const { - Unimplemented(); + if (fr.is_interpreted_frame()) { + fr.set_offset_fp(relativize_address(fr.fp())); + } } inline void stackChunkOopDesc::derelativize_frame_pd(frame& fr) const { - Unimplemented(); + if (fr.is_interpreted_frame()) { + fr.set_fp(derelativize_address(fr.offset_fp())); + } } #endif // CPU_S390_STACKCHUNKOOP_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp index d0e26beedab..d773b6ce759 100644 --- a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp +++ b/src/hotspot/cpu/s390/stubDeclarations_s390.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. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -47,7 +47,7 @@ do_arch_entry, \ do_arch_entry_init, \ do_arch_entry_array) \ - do_arch_blob(continuation, 2000) \ + do_arch_blob(continuation, 5000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index 5309158fc74..381d1c02277 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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 +38,8 @@ #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" #include "prims/upcallLinker.hpp" +#include "runtime/continuation.hpp" +#include "runtime/continuationEntry.inline.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" @@ -330,6 +333,8 @@ class StubGenerator: public StubCodeGenerator { // Pop frame. Done here to minimize stalls. __ pop_frame(); + __ pop_cont_fastpath(); + // Reload some volatile registers which we've spilled before the call // to template interpreter / native entry. // Access all locals via frame pointer, because we know nothing about @@ -3223,28 +3228,182 @@ class StubGenerator: public StubCodeGenerator { return start; } - address generate_cont_thaw(bool return_barrier, bool exception) { + address generate_cont_thaw(StubId stub_id) { if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + + Continuation::thaw_kind kind; + bool return_barrier; + bool return_barrier_exception; + + switch (stub_id) { + case StubId::stubgen_cont_thaw_id: + kind = Continuation::thaw_top; + return_barrier = false; + return_barrier_exception = false; + break; + case StubId::stubgen_cont_returnBarrier_id: + kind = Continuation::thaw_return_barrier; + return_barrier = true; + return_barrier_exception = false; + break; + case StubId::stubgen_cont_returnBarrierExc_id: + kind = Continuation::thaw_return_barrier_exception; + return_barrier = true; + return_barrier_exception = true; + break; + default: + ShouldNotReachHere(); + } + + StubCodeMark mark(this, stub_id); + address start = __ pc(); + + // TODO: Handle Valhalla return types. May require generating different return barriers. + + if (kind == Continuation::thaw_top) { + __ clobber_nonvolatile_registers(); // Except Z_thread + } + + if (return_barrier) { + // Save return values in non-volatile float registers to preserve them across VM calls. + // Z_F8 and Z_F9 are non-volatile (callee-saved) registers on s390 (F8-F15 are non-volatile). + // They are safe to use here because: + // 1. clobber_nonvolatile_registers() is NOT called for return_barrier cases (only for thaw_top) + // 2. These registers are preserved across the VM leaf calls (prepare_thaw, thaw_entry) + __ z_ldgr(Z_F8, Z_RET); // Save integer return value in non-volatile float register + __ z_ldr(Z_F9, Z_FRET); // Save float return value in non-volatile float register + + DEBUG_ONLY(__ z_lg(Z_R1_scratch, _z_common_abi(callers_sp), Z_SP);) + __ z_lg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); +#ifdef ASSERT + __ z_cg(Z_R1_scratch, _z_common_abi(callers_sp), Z_SP); + __ asm_assert(/* check_equal=*/ true, FILE_AND_LINE ": callers sp is corrupt at thaw entry", 69); +#endif + + } + +#ifdef ASSERT + __ z_cg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + __ asm_assert(/* check_equal=*/ true, FILE_AND_LINE ": incorrect Z_SP", 70); +#endif + + __ z_lghi(Z_ARG2, return_barrier ? 1 : 0); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Continuation::prepare_thaw), Z_thread, Z_ARG2); + +#ifdef ASSERT + __ z_cg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + __ asm_assert(/* check equal = */ true, FILE_AND_LINE ": incorrect Z_SP after prepare_thaw", 48); +#endif // ASSERT + + // Z_RET contains the size of the frames to thaw, 0 if overflow or no more frames + NearLabel L_thaw_success; + __ z_ltgr(Z_RET, Z_RET); + __ branch_optimized(Assembler::bcondNotEqual, L_thaw_success); + __ load_const_optimized(Z_R1_scratch, (SharedRuntime::throw_StackOverflowError_entry())); + __ call(Z_R1_scratch); + __ bind(L_thaw_success); + + // Make room for the thawed frames and align the stack. + __ add64(Z_RET, frame::z_abi_160_size); + +#ifdef ASSERT + __ z_tmll(Z_RET, frame::alignment_in_bytes - 1); + __ asm_assert(Assembler::bcondAllZero, FILE_AND_LINE ": size is not aligned properly", 71); +#endif // ASSERT + + __ z_lcgr(Z_RET, Z_RET); // negate Z_RET value + __ resize_frame( /* offset = */ Z_RET,/* fp = */ Z_R1, /* load_fp = */ true); + + __ z_lghi(Z_ARG2, kind); + __ add64(Z_SP, -frame::z_abi_160_size); // Register save area for Continuation::thaw + __ call_VM_leaf(Continuation::thaw_entry(), Z_thread, Z_ARG2); + __ z_lgr(Z_SP, Z_RET); // Z_RET contains the SP of the thawed top frame + + if (return_barrier) { + // we're now in the caller of the frame that returned to the barrier + // restore return value (no safepoint in the call to thaw, so even an oop return value should be OK) + + __ z_lgdr(Z_RET, Z_F8); // Restore integer return value + __ z_ldr(Z_FRET, Z_F9); // Restore float return value + } else { + // we're now on the yield frame (which is in an address above us b/c rsp has been pushed down) + __ z_lghi(Z_RET, 0); // return 0 (success) from doYield + } + + if (return_barrier_exception) { + Register handler = Z_R1_scratch; + __ z_lg(Z_ARG2, _z_common_abi(return_pc), Z_SP); // exception pc + __ save_return_pc(); + __ push_frame_abi160(0 + 2 * BytesPerWord); + __ z_stg(Z_RET , 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save return value containing the exception oop + + __ z_stg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save exception_pc + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), Z_thread, Z_ARG2); + + // Copy handler's address. + __ z_lgr(handler, Z_RET); + + // Set up the arguments for the exception handler: + // - Z_ARG1: exception oop + // - Z_ARG2: exception pc + __ z_lg(Z_ARG1, 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception oop + __ z_lg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception pc + __ pop_frame(); + __ restore_return_pc(); + } else { + // We're "returning" into the topmost thawed frame; see Thaw::push_return_frame + __ z_lg(Z_R1_scratch, _z_common_abi(return_pc), Z_SP); + } + __ z_br(Z_R1_scratch); + + return start; } address generate_cont_thaw() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + return generate_cont_thaw(StubId::stubgen_cont_thaw_id); } address generate_cont_returnBarrier() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + return generate_cont_thaw(StubId::stubgen_cont_returnBarrier_id); } address generate_cont_returnBarrier_exception() { + return generate_cont_thaw(StubId::stubgen_cont_returnBarrierExc_id); + } + + address generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + StubId stub_id = StubId::stubgen_cont_preempt_id; + StubCodeMark mark(this, stub_id); + address start = __ pc(); + + __ clobber_nonvolatile_registers(); // Except Z_thread + + __ reset_last_Java_frame(/*check_last_java_sp=*/ false); + + // Set sp to enterSpecial frame, i.e. remove all frames copied into the heap. + __ z_lg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + + Label preemption_cancelled; + + __ z_cli(in_bytes(JavaThread::preemption_cancelled_offset()), Z_thread, 0); + __ z_brne(preemption_cancelled); + + // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. + SharedRuntime::continuation_enter_cleanup(_masm); + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + // We acquired the monitor after freezing the frames so call thaw to continue execution. + __ bind(preemption_cancelled); + __ z_mvi(in_bytes(JavaThread::preemption_cancelled_offset()), Z_thread, 0); + + __ load_const_optimized(Z_R1, ContinuationEntry::thaw_call_pc_address()); + __ z_lg(Z_R1, Address(Z_R1)); + __ z_br(Z_R1); + + return start; } // exception handler for upcall stubs @@ -3327,9 +3486,10 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return; // Continuation stubs: - StubRoutines::_cont_thaw = generate_cont_thaw(); - StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); + StubRoutines::_cont_thaw = generate_cont_thaw(); + StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); + StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); } void generate_final_stubs() { diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index dba04fc0e85..03470597ab5 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_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. * @@ -717,13 +717,31 @@ address TemplateInterpreterGenerator::generate_safept_entry_for (TosState state, address runtime_entry) { address entry = __ pc(); __ push(state); + __ push_cont_fastpath(); __ call_VM(noreg, runtime_entry); + __ pop_cont_fastpath(); __ dispatch_via(vtos, Interpreter::_normal_table.table_for (vtos)); return entry; } address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { - return nullptr; + if (!Continuations::enabled()) return nullptr; + address start = __ pc(); + __ z_lg(Z_fp, _z_common_abi(callers_sp), Z_SP); + { + Register top_frame_sp = Z_R1_scratch; // anyway going to load it with correct value + __ z_lg(top_frame_sp, Address(Z_fp, _z_ijava_state_neg(top_frame_sp))); + __ z_slag(top_frame_sp, top_frame_sp, Interpreter::logStackElementSize); + __ z_agr(top_frame_sp, Z_fp); + + __ resize_frame_absolute(top_frame_sp, /* temp = */ Z_R0, /* load_fp = */ true); + } + __ restore_bcp(); + __ restore_locals(); + __ restore_esp(); + + __ z_br(Z_R14); + return start; } @@ -1468,8 +1486,13 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ bind(call_signature_handler); + bool support_vthread_preemption = Continuations::enabled(); + // We have a TOP_IJAVA_FRAME here, which belongs to us. - __ set_top_ijava_frame_at_SP_as_last_Java_frame(Z_SP, Z_R1/*tmp*/); + Label last_java_pc; + Label *resume_pc = support_vthread_preemption ? &last_java_pc : nullptr; + + __ set_top_ijava_frame_at_SP_as_last_Java_frame(Z_SP, Z_R1/*tmp*/, resume_pc); // Call signature handler and pass locals address in Z_ARG1. __ z_lgr(Z_ARG1, Z_locals); @@ -1526,7 +1549,18 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // overwritten since "__ call_stub(signature_handler);" (except for // ARG1 and ARG2 for static methods). + if (support_vthread_preemption) { + // Rresult_handler is a nonvolatile register. Its value will be preserved across + // the native call but only if the call isn't preempted. To preserve its value even + // in the case of preemption we save it in the lresult slot. It is restored at + // resume_pc if, and only if the call was preempted. This works because only + // j.l.Object::wait calls are preempted which don't return a result. + + __ z_stg(Rresult_handler, _z_ijava_state_neg(lresult), Z_fp); + } + __ push_cont_fastpath(); __ call_c(Z_R1/*native_method_entry*/); + __ pop_cont_fastpath(); // NOTE: frame::interpreter_frame_result() depends on these stores. __ z_stg(Z_RET, _z_ijava_state_neg(lresult), Z_fp); @@ -1610,6 +1644,32 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ z_lg(Z_bcp, Address(Rmethod, Method::const_offset())); // get constMethod __ add2reg(Z_bcp, in_bytes(ConstMethod::codes_offset())); // get codebase + if (support_vthread_preemption) { + // Check preemption for Object.wait() + Label not_preempted; + __ z_ltg(Z_R1_scratch, Address(Z_thread, JavaThread::preempt_alternate_return_offset())); + __ z_brz(not_preempted); // if 0, jump to not_preempted + __ z_mvghi(Address(Z_thread, JavaThread::preempt_alternate_return_offset()), 0); + __ z_br(Z_R1_scratch); + + // Execution will be resumed here when the vthread becomes runnable again. + __ bind(*resume_pc); + __ restore_after_resume(); + // We saved the result handler before the call + __ z_lg(Rresult_handler, _z_ijava_state_neg(lresult), Z_fp); +#ifdef ASSERT + // Clobber result slots. Only native methods returning void can be preemted currently. + __ load_const(Z_RET, UCONST64(0xbad01001)); + __ z_stg(Z_RET, _z_ijava_state_neg(lresult), Z_fp); + __ z_stg(Z_RET, _z_ijava_state_neg(fresult), Z_fp); + // reset_last_Java_frame() below asserts that a last java sp is set + __ asm_assert_mem8_is_zero(in_bytes(JavaThread::last_Java_sp_offset()), + Z_thread, FILE_AND_LINE ": Last java sp should not be set when resuming", 69); + __ z_stg(Z_RET, in_bytes(JavaThread::last_Java_sp_offset()), Z_thread); +#endif + __ bind(not_preempted); + } + if (CheckJNICalls) { // clear_pending_jni_exception_check __ clear_mem(Address(Z_thread, JavaThread::pending_jni_exception_check_fn_offset()), sizeof(oop)); @@ -2030,7 +2090,7 @@ address TemplateInterpreterGenerator::generate_CRC32C_updateBytes_entry(Abstract address TemplateInterpreterGenerator::generate_currentThread() { uint64_t entry_off = __ offset(); - __ z_lg(Z_RET, Address(Z_thread, JavaThread::threadObj_offset())); + __ z_lg(Z_RET, Address(Z_thread, JavaThread::vthread_offset())); __ resolve_oop_handle(Z_RET, Z_R0_scratch, Z_R1_scratch); // Restore caller sp for c2i case. @@ -2176,6 +2236,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { JavaThread::popframe_force_deopt_reexecution_bit, Z_tmp_1, false); + __ pop_cont_fastpath(); // Continue in deoptimization handler. __ z_br(Z_R14); @@ -2191,6 +2252,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { false, // install_monitor_exception false); // notify_jvmdi __ z_lg(Z_fp, _z_abi(callers_sp), Z_SP); // Restore frame pointer. + __ pop_cont_fastpath(); { Register top_frame_sp = Z_R1_scratch; __ z_lg(top_frame_sp, Address(Z_fp, _z_ijava_state_neg(top_frame_sp))); @@ -2264,6 +2326,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // Remove the activation (without doing throws on illegalMonitorExceptions). __ remove_activation(vtos, noreg/*ret.pc already loaded*/, false/*throw exc*/, true/*install exc*/, false/*notify jvmti*/); __ z_lg(Z_fp, _z_abi(callers_sp), Z_SP); // Restore frame pointer. + __ pop_cont_fastpath(); __ get_vm_result_oop(Z_ARG1); // Restore exception. __ verify_oop(Z_ARG1); diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 3b0929608a3..1db9c54aef5 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -1055,7 +1055,7 @@ void TemplateTable::lstore() { void TemplateTable::fstore() { transition(ftos, vtos); locals_index(Z_R1_scratch); - __ freg2mem_opt(Z_ftos, faddress(_masm, Z_R1_scratch)); + __ freg2mem_opt(Z_ftos, faddress(_masm, Z_R1_scratch), false); } void TemplateTable::dstore() { @@ -2336,7 +2336,9 @@ void TemplateTable::_return(TosState state) { __ z_tm(poll_byte_addr, SafepointMechanism::poll_bit()); __ z_braz(no_safepoint); __ push(state); + __ push_cont_fastpath(); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)); + __ pop_cont_fastpath(); __ pop(state); __ bind(no_safepoint); } @@ -2395,7 +2397,7 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, // Class initialization barrier slow path lands here as well. address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); __ load_const_optimized(Z_ARG2, (int)code); - __ call_VM(noreg, entry, Z_ARG2); + __ call_VM_preemptable(noreg, entry, Z_ARG2); // Update registers with resolved info. __ load_method_entry(Rcache, index); @@ -2445,7 +2447,7 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, // Class initialization barrier slow path lands here as well. address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); __ load_const_optimized(Z_ARG2, (int)code); - __ call_VM(noreg, entry, Z_ARG2); + __ call_VM_preemptable(noreg, entry, Z_ARG2); // Update registers with resolved info. __ load_field_entry(cache, index); @@ -3504,7 +3506,7 @@ void TemplateTable::fast_xaccess(TosState state) { __ verify_oop(Z_tos); break; case ftos: - __ mem2freg_opt(Z_ftos, field); + __ mem2freg_opt(Z_ftos, field, false); break; default: ShouldNotReachHere(); @@ -4022,7 +4024,7 @@ void TemplateTable::_new() { __ bind(slow_case); __ get_constant_pool(Z_ARG2); __ get_2_byte_integer_at_bcp(Z_ARG3/*dest*/, 1, InterpreterMacroAssembler::Unsigned); - call_VM(Z_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), Z_ARG2, Z_ARG3); + __ call_VM_preemptable(Z_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), Z_ARG2, Z_ARG3); __ verify_oop(Z_tos); // continue diff --git a/src/hotspot/cpu/s390/upcallLinker_s390.cpp b/src/hotspot/cpu/s390/upcallLinker_s390.cpp index 23ac80ddf48..de57e5e0cc4 100644 --- a/src/hotspot/cpu/s390/upcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/upcallLinker_s390.cpp @@ -220,9 +220,13 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, __ call(call_target_address); // load taget Method* into Z_method __ block_comment("} load_target"); + __ push_cont_fastpath(); + __ z_lg(call_target_address, Address(Z_method, in_bytes(Method::from_compiled_offset()))); __ call(call_target_address); + __ pop_cont_fastpath(); + // return value shuffle assert(!needs_return_buffer, "unexpected needs_return_buffer"); // CallArranger can pick a return type that goes in the same reg for both CCs. diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 5c05b3702bb..1f660d6d349 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -697,7 +697,9 @@ void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmi } break; - case T_BOOLEAN: // fall through + case T_BOOLEAN: + __ movb(as_Address(addr), c->as_jint() & 1); + break; case T_BYTE: __ movb(as_Address(addr), c->as_jint() & 0xFF); break; diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index 2d382c7e5e5..50f0c6b7eb6 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_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 @@ -54,7 +54,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, @@ -101,8 +100,7 @@ // between a callee frame and its stack arguments, where it is part // of the caller/callee overlap metadata_words_at_top = 0, - // size, in words, of frame metadata at the frame top that needs - // to be reserved for callee functions in the runtime + // in bytes frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 @@ -172,8 +170,6 @@ // deoptimization support void interpreter_frame_set_last_sp(intptr_t* sp); - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index 731eef09c37..265d9b16397 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -357,8 +357,8 @@ void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register __ jcc(Assembler::notZero, error); // make sure klass is 'reasonable', which is not zero. - __ load_klass(obj, obj, tmp1); // get klass - __ testptr(obj, obj); + __ load_narrow_klass(tmp1, obj); // get narrow Klass + __ testl(tmp1, tmp1); __ jcc(Assembler::zero, error); // if klass is null it is broken } diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index fdf10e5b5e6..480e484f4b1 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -154,11 +154,7 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, Label runtime; assert(pre_val != noreg, "check this code"); - - if (obj != noreg) { - assert_different_registers(obj, pre_val, tmp); - assert(pre_val != rax, "check this code"); - } + assert_different_registers(obj, pre_val, tmp); Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); @@ -511,6 +507,32 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ bind(done); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) { + // Check if the oop is in the right area of memory + __ movptr(tmp1, obj); + __ movptr(tmp2, (intptr_t) Universe::verify_oop_mask()); + __ andptr(tmp1, tmp2); + __ movptr(tmp2, (intptr_t) Universe::verify_oop_bits()); + __ cmpptr(tmp1, tmp2); + __ jcc(Assembler::notZero, L_error); + + // This routine is sometimes called before applying GC barriers. + // With +COH, loading the klass may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + Address gc_state(r15_thread, ShenandoahThreadLocalData::gc_state_offset()); + __ testb(gc_state, ShenandoahHeap::HAS_FORWARDED); + __ jcc(Assembler::notZero, L_skip); + } + + // Make sure klass is 'reasonable', which is not zero. + __ load_narrow_klass(tmp1, obj); + __ testl(tmp1, tmp1); + __ jcc(Assembler::zero, L_error); + + __ bind(L_skip); +} + #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ #else @@ -560,99 +582,42 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. - - __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); - - Register pre_val_reg = stub->pre_val()->as_register(); - - if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/); - } - - __ cmpptr(pre_val_reg, NULL_WORD); - __ jcc(Assembler::equal, *stub->continuation()); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); - __ jmp(*stub->continuation()); - -} - -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); + + Register obj = stub->obj()->as_register(); + + if (stub->do_load()) { + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false); + } + __ cmpptr(obj, NULL_WORD); + __ jcc(Assembler::equal, *stub->continuation()); + + ce->store_parameter(obj, 0); + __ call(RuntimeAddress(bs->keepalive_barrier_stub())); + __ jmp(*stub->continuation()); +} + +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { + __ bind(*stub->entry()); + + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - assert_different_registers(obj, res, addr, tmp1, tmp2); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == rax, "C1 must know about our slow call result register"); - Label slow_path; - - assert(res == rax, "result must arrive in rax"); - - if (res != obj) { - __ mov(res, obj); - } - - if (is_strong) { - // Check for object being in the collection set. - __ mov(tmp1, res); - if (AOTCodeCache::is_on_for_dump()) { - __ push(rcx); - __ lea(rcx, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); - __ movl(rcx, Address(rcx)); - if (tmp1 != rcx) { - __ mov(tmp1, res); - __ shrptr(tmp1); - __ pop(rcx); - } else { - assert_different_registers(tmp2, rcx); - __ mov(tmp2, res); - __ shrptr(tmp2); - __ pop(rcx); - __ movptr(tmp1, tmp2); - } - __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address())); - __ movptr(tmp2, Address(tmp2)); - } else { - __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); - } - __ movbool(tmp2, Address(tmp2, tmp1, Address::times_1)); - __ testbool(tmp2); - __ jcc(Assembler::zero, *stub->continuation()); - } - - __ bind(slow_path); - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - if (is_strong) { - if (is_native) { - __ call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mov(obj, slow_result); } + __ jmp(*stub->continuation()); } @@ -660,98 +625,28 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - // arg0 : previous value of memory - - __ push(rax); - __ push(rdx); - - const Register pre_val = rax; - const Register thread = r15_thread; +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = rax; const Register tmp = rdx; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is SATB still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ testb(gc_state, ShenandoahHeap::MARKING); - __ jcc(Assembler::zero, done); - - // Can we store original value in the thread's buffer? - - __ movptr(tmp, queue_index); - __ testptr(tmp, tmp); - __ jcc(Assembler::zero, runtime); - __ subptr(tmp, wordSize); - __ movptr(queue_index, tmp); - __ addptr(tmp, buffer); - - // prev_val (rax) - __ load_parameter(0, pre_val); - __ movptr(Address(tmp, 0), pre_val); - __ jmp(done); - - __ bind(runtime); - - __ save_live_registers_no_oop_map(true); - - // load the pre-value - __ load_parameter(0, rcx); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), rcx); - - __ restore_live_registers(true); - - __ bind(done); - - __ pop(rdx); - __ pop(rax); - + __ push(tmp); + __ push(tmp_obj); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, tmp); + __ pop(tmp_obj); + __ pop(tmp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ save_live_registers_no_oop_map(true); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - __ load_parameter(0, c_rarg0); - __ load_parameter(1, c_rarg1); - if (is_strong) { - if (is_native) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); - } else { - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), c_rarg0, c_rarg1); - } - - __ restore_live_registers_except_rax(true); - + const Register tmp_obj = rax; + const Register tmp_addr = rdx; + __ push(tmp_addr); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop(tmp_addr); __ epilogue(); } @@ -810,10 +705,9 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac assert(oldval == rax, "must be in rax for implicit use in cmpxchg"); - // Oldval and newval can be in the same register, but all other registers should be - // distinct for extra safety, as we shuffle register values around. - assert_different_registers(oldval, tmp, addr.base(), addr.index()); - assert_different_registers(newval, tmp, addr.base(), addr.index()); + // Oldval and newval cannot be clobbered by aliasing with tmp. + assert_different_registers(oldval, tmp); + assert_different_registers(newval, tmp); ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow); @@ -834,7 +728,7 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac } void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register newval, Address addr, Register tmp, bool narrow) { - assert_different_registers(newval, tmp, addr.base(), addr.index()); + assert_different_registers(newval, tmp); ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index f608760ce42..7c1a89b74f5 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -33,7 +33,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -71,12 +71,14 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Label& slowpath); + virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 3301d6ace49..12e9cfa4573 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -1551,8 +1551,8 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe __ bind(check_oop); // make sure klass is 'reasonable', which is not zero. - __ load_klass(tmp1, obj, tmp2); // get klass - __ testptr(tmp1, tmp1); + __ load_narrow_klass(tmp1, obj); // get narrow klass + __ testl(tmp1, tmp1); __ jcc(Assembler::zero, error); // if klass is null it is broken __ bind(check_zaddress); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 0ac0a8243d4..6c0b1178b0e 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5434,17 +5434,19 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { shrq(dst, markWord::klass_shift); } +void MacroAssembler::load_narrow_klass(Register dst, Register src) { + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(dst, src); + } else { + movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); + } +} + void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { assert_different_registers(src, tmp); assert_different_registers(dst, tmp); - - if (UseCompactObjectHeaders) { - load_narrow_klass_compact(dst, src); - decode_klass_not_null(dst, tmp); - } else { - movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); - decode_klass_not_null(dst, tmp); - } + load_narrow_klass(dst, src); + decode_klass_not_null(dst, tmp); } void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { @@ -5872,7 +5874,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X // cnt - number of qwords (8-byte words). // base - start address, qword aligned. Label L_zero_64_bytes, L_loop, L_sloop, L_tail, L_end; - bool use64byteVector = (MaxVectorSize == 64) && (CopyAVX3Threshold == 0); + bool use64byteVector = (MaxVectorSize == 64) && (CopyAVX3Threshold == 0) && VM_Version::supports_bmi2(); if (use64byteVector) { vpxor(xtmp, xtmp, xtmp, AVX_512bit); } else if (MaxVectorSize >= 32) { @@ -5919,7 +5921,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X BIND(L_tail); addptr(cnt, 4); jccb(Assembler::lessEqual, L_end); - if (UseAVX > 2 && MaxVectorSize >= 32 && VM_Version::supports_avx512vl()) { + if (UseAVX > 2 && MaxVectorSize >= 32 && VM_Version::supports_avx512vl() && VM_Version::supports_bmi2()) { fill32_masked(3, base, 0, xtmp, mask, cnt, rtmp); } else { decrement(cnt); @@ -6982,7 +6984,7 @@ void MacroAssembler::vectorized_mismatch(Register obja, Register objb, Register xorq(result, result); if ((AVX3Threshold == 0) && (UseAVX > 2) && - VM_Version::supports_avx512vlbw() && UseCountTrailingZerosInstruction) { + VM_Version::supports_avx512vlbw() && UseCountTrailingZerosInstruction && VM_Version::supports_bmi2()) { Label VECTOR64_LOOP, VECTOR64_NOT_EQUAL, VECTOR32_TAIL; cmpq(length, 64); @@ -10106,10 +10108,6 @@ void MacroAssembler::load_aotrc_address(Register reg, address a) { } void MacroAssembler::setcc(Assembler::Condition comparison, Register dst) { - if (VM_Version::supports_apx_f()) { - esetzucc(comparison, dst); - } else { - setb(comparison, dst); - movzbl(dst, dst); - } + setb(comparison, dst); + movzbl(dst, dst); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index de5ec02fe43..a74c7b16f3e 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -350,6 +350,7 @@ class MacroAssembler: public Assembler { // oop manipulations void load_narrow_klass_compact(Register dst, Register src); + void load_narrow_klass(Register dst, Register src); void load_klass(Register dst, Register src, Register tmp); void store_klass(Register dst, Register src, Register tmp); diff --git a/src/hotspot/cpu/x86/methodHandles_x86.hpp b/src/hotspot/cpu/x86/methodHandles_x86.hpp index c4dde903d29..8fdb6c1fb52 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.hpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.hpp @@ -27,7 +27,7 @@ // Adapters enum /* platform_dependent_constants */ { - adapter_code_size = 6000 DEBUG_ONLY(+ 6000) + adapter_code_size = 8000 DEBUG_ONLY(+ 6000) }; // Additional helper methods for MethodHandles code generation: diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 8bb9982a820..7fc105046ff 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -3452,7 +3452,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { }; const char* name = SharedRuntime::stub_name(StubId::shared_jfr_write_checkpoint_id); - CodeBuffer code(name, 1024, 64); + CodeBuffer code(name, 1024 + (UseAPX ? 1024 : 0), 64); MacroAssembler* masm = new MacroAssembler(&code); address start = __ pc(); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index afd9c126a21..2b37e39ec86 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -3069,7 +3069,7 @@ address StubGenerator::generate_base64_decodeBlock() { // If AVX512 VBMI not supported, just compile non-AVX code if(VM_Version::supports_avx512_vbmi() && - VM_Version::supports_avx512bw()) { + VM_Version::supports_avx512bw() && VM_Version::supports_bmi2()) { __ cmpl(length, 31); // 32-bytes is break-even for AVX-512 __ jcc(Assembler::lessEqual, L_lastChunk); @@ -4887,7 +4887,7 @@ void StubGenerator::generate_compiler_stubs() { StubRoutines::_data_cache_writeback = generate_data_cache_writeback(); StubRoutines::_data_cache_writeback_sync = generate_data_cache_writeback_sync(); - if ((UseAVX == 2) && EnableX86ECoreOpts && UseCountTrailingZerosInstruction) { + if ((UseAVX == 2) && EnableX86ECoreOpts && UseCountTrailingZerosInstruction && VM_Version::supports_bmi2()) { generate_string_indexof(StubRoutines::_string_indexof_array); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index e7dc416a961..cececa7b3ad 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -3560,23 +3560,24 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh __ testl(r11_length, r11_length); __ jccb(Assembler::negative, L_failed_0); - __ load_klass(r10_src_klass, src, rklass_tmp); + __ load_narrow_klass(r10_src_klass, src); #ifdef ASSERT // assert(src->klass() != nullptr); { BLOCK_COMMENT("assert klasses not null {"); Label L1, L2; - __ testptr(r10_src_klass, r10_src_klass); + __ testl(r10_src_klass, r10_src_klass); __ jcc(Assembler::notZero, L2); // it is broken if klass is null __ bind(L1); __ stop("broken null klass"); __ bind(L2); - __ load_klass(rax, dst, rklass_tmp); - __ cmpq(rax, 0); + __ load_narrow_klass(rax, dst); + __ testl(rax, rax); __ jcc(Assembler::equal, L1); // this would be broken also BLOCK_COMMENT("} assert klasses not null done"); } #endif + __ decode_klass_not_null(r10_src_klass, rklass_tmp); // Load layout helper (32-bits) // diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index c35a2a1bba6..840f848d3ba 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp @@ -1108,15 +1108,13 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, void StubGenerator::generate_kyber_stubs() { // Generate Kyber intrinsics code if (UseKyberIntrinsics) { - if (VM_Version::supports_evex()) { - StubRoutines::_kyberNtt = generate_kyberNtt_avx512(this, _masm); - StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt_avx512(this, _masm); - StubRoutines::_kyberNttMult = generate_kyberNttMult_avx512(this, _masm); - StubRoutines::_kyberAddPoly_2 = generate_kyberAddPoly_2_avx512(this, _masm); - StubRoutines::_kyberAddPoly_3 = generate_kyberAddPoly_3_avx512(this, _masm); - StubRoutines::_kyber12To16 = generate_kyber12To16_avx512(this, _masm); - StubRoutines::_kyberBarrettReduce = generate_kyberBarrettReduce_avx512(this, _masm); - } + StubRoutines::_kyberNtt = generate_kyberNtt_avx512(this, _masm); + StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt_avx512(this, _masm); + StubRoutines::_kyberNttMult = generate_kyberNttMult_avx512(this, _masm); + StubRoutines::_kyberAddPoly_2 = generate_kyberAddPoly_2_avx512(this, _masm); + StubRoutines::_kyberAddPoly_3 = generate_kyberAddPoly_3_avx512(this, _masm); + StubRoutines::_kyber12To16 = generate_kyber12To16_avx512(this, _masm); + StubRoutines::_kyberBarrettReduce = generate_kyberBarrettReduce_avx512(this, _masm); } } diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 2ca1c172542..e395dd301f4 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -489,6 +489,25 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ jmp(wrapup); __ bind(start_simd_check); + // Query CPUID 0xD sub-leaf 5, 6, and 7 offsets for AVX-512 XSAVE components + __ movl(rax, 0xD); + __ movl(rcx, 5); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::opmask_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + + __ movl(rax, 0xD); + __ movl(rcx, 6); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm0to15_hi256_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + + __ movl(rax, 0xD); + __ movl(rcx, 7); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm16to31_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + // // Some OSs have a bug when upper 128/256bits of YMM/ZMM // registers are not restored after a signal processing. @@ -1251,7 +1270,7 @@ void VM_Version::get_processor_features() { // Kyber Intrinsics // Currently we only have them for AVX512 - if (supports_evex() && supports_avx512bw()) { + if (supports_avx512vlbw()) { if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { UseKyberIntrinsics = true; } @@ -1324,7 +1343,7 @@ void VM_Version::get_processor_features() { } if (UseSHA && ((supports_evex() && supports_avx512vlbw()) || - (EnableX86ECoreOpts && !supports_hybrid()))) { + (supports_avx2() && EnableX86ECoreOpts && !supports_hybrid()))) { if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 2fb1af71a10..d268665d091 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -683,6 +683,11 @@ protected: uint32_t apx_xstate_size; // EAX: size of APX state (128) uint32_t apx_xstate_offset; // EBX: offset in standard XSAVE area + // cpuid function 0xD, subleaf 5, 6 and 7 (AVX-512 extended state) + uint32_t opmask_xstate_offset; // EBX: offset of Opmask component + uint32_t zmm0to15_hi256_xstate_offset; // EBX: offset of ZMM_Hi256 component + uint32_t zmm16to31_xstate_offset; // EBX: offset of Hi16_ZMM component + VM_Features feature_flags() const; // Asserts @@ -748,9 +753,15 @@ public: static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); } static ByteSize apx_xstate_offset_offset() { return byte_offset_of(CpuidInfo, apx_xstate_offset); } static ByteSize apx_xstate_size_offset() { return byte_offset_of(CpuidInfo, apx_xstate_size); } + static ByteSize opmask_xstate_offset_offset() { return byte_offset_of(CpuidInfo, opmask_xstate_offset); } + static ByteSize zmm0to15_hi256_xstate_offset_offset() { return byte_offset_of(CpuidInfo, zmm0to15_hi256_xstate_offset); } + static ByteSize zmm16to31_xstate_offset_offset() { return byte_offset_of(CpuidInfo, zmm16to31_xstate_offset); } static uint32_t apx_xstate_offset() { return _cpuid_info.apx_xstate_offset; } static uint32_t apx_xstate_size() { return _cpuid_info.apx_xstate_size; } + static uint32_t opmask_xstate_offset() { return _cpuid_info.opmask_xstate_offset; } + static uint32_t zmm0to15_hi256_xstate_offset() { return _cpuid_info.zmm0to15_hi256_xstate_offset; } + static uint32_t zmm16to31_xstate_offset() { return _cpuid_info.zmm16to31_xstate_offset; } // The value used to check ymm register after signal handle static int ymm_test_value() { return 0xCAFEBABE; } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index b3dd1a0812b..3f953dbe725 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2764,23 +2764,23 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? default_float_pressure_threshold : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { return INT_RAX_REG_mask(); } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { return INT_RDX_REG_mask(); } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { return LONG_RAX_REG_mask(); } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { return LONG_RDX_REG_mask(); } @@ -3179,6 +3179,13 @@ bool Matcher::match_rule_supported(int opcode) { break; case Op_VectorCmpMasked: + if (!UseCountTrailingZerosInstruction) { + return false; + } + if (UseAVX < 3 || !VM_Version::supports_bmi2()) { + return false; + } + break; case Op_VectorMaskGen: if (UseAVX < 3 || !VM_Version::supports_bmi2()) { return false; @@ -7287,13 +7294,8 @@ instruct loadNKlassCompactHeaders(rRegN dst, memory mem, rFlagsReg cr) "shrl $dst, markWord::klass_shift_at_offset" %} ins_encode %{ - if (UseAPX) { - __ eshrl($dst$$Register, $mem$$Address, markWord::klass_shift_at_offset, false); - } - else { - __ movl($dst$$Register, $mem$$Address); - __ shrl($dst$$Register, markWord::klass_shift_at_offset); - } + __ movl($dst$$Register, $mem$$Address); + __ shrl($dst$$Register, markWord::klass_shift_at_offset); %} ins_pipe(ialu_reg_mem); %} @@ -9269,7 +9271,6 @@ instruct cmovI_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) // Conditional move instruct cmovI_mem(cmpOp cop, rFlagsReg cr, rRegI dst, memory src) %{ - predicate(!UseAPX); match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); ins_cost(250); // XXX @@ -9280,24 +9281,9 @@ instruct cmovI_mem(cmpOp cop, rFlagsReg cr, rRegI dst, memory src) %{ ins_pipe(pipe_cmov_mem); %} -// Conditional move -instruct cmovI_rReg_rReg_mem_ndd(rRegI dst, cmpOp cop, rFlagsReg cr, rRegI src1, memory src2) -%{ - predicate(UseAPX); - match(Set dst (CMoveI (Binary cop cr) (Binary src1 (LoadI src2)))); - - ins_cost(250); - format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, int ndd" %} - ins_encode %{ - __ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address); - %} - ins_pipe(pipe_cmov_mem); -%} - // Conditional move instruct cmovI_memU(cmpOpU cop, rFlagsRegU cr, rRegI dst, memory src) %{ - predicate(!UseAPX); match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); ins_cost(250); // XXX @@ -9317,27 +9303,13 @@ instruct cmovI_memUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegI dst, memory src) %{ %} %} -instruct cmovI_rReg_rReg_memU_ndd(rRegI dst, cmpOpU cop, rFlagsRegU cr, rRegI src1, memory src2) -%{ - predicate(UseAPX); - match(Set dst (CMoveI (Binary cop cr) (Binary src1 (LoadI src2)))); +instruct cmovI_memUCFE(cmpOpUCFE cop, rFlagsRegUCFE cr, rRegI dst, memory src) %{ + match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src)))); - ins_cost(250); - format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, int ndd" %} + ins_cost(250); // XXX + format %{ "cmovl$cop $dst, $src\t# unsigned, int" %} ins_encode %{ - __ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address); - %} - ins_pipe(pipe_cmov_mem); -%} - -instruct cmovI_rReg_rReg_memUCFE_ndd(rRegI dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegI src1, memory src2) -%{ - match(Set dst (CMoveI (Binary cop cr) (Binary src1 (LoadI src2)))); - - ins_cost(250); - format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, int ndd" %} - ins_encode %{ - __ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address); + __ cmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src$$Address); %} ins_pipe(pipe_cmov_mem); %} @@ -9596,7 +9568,6 @@ instruct cmovL_reg_ndd(rRegL dst, cmpOp cop, rFlagsReg cr, rRegL src1, rRegL src instruct cmovL_mem(cmpOp cop, rFlagsReg cr, rRegL dst, memory src) %{ - predicate(!UseAPX); match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src)))); ins_cost(200); // XXX @@ -9607,19 +9578,6 @@ instruct cmovL_mem(cmpOp cop, rFlagsReg cr, rRegL dst, memory src) ins_pipe(pipe_cmov_mem); // XXX %} -instruct cmovL_rReg_rReg_mem_ndd(rRegL dst, cmpOp cop, rFlagsReg cr, rRegL src1, memory src2) -%{ - predicate(UseAPX); - match(Set dst (CMoveL (Binary cop cr) (Binary src1 (LoadL src2)))); - - ins_cost(200); - format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, long ndd" %} - ins_encode %{ - __ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address); - %} - ins_pipe(pipe_cmov_mem); -%} - instruct cmovL_imm_01U(rRegL dst, immL1 src, rFlagsRegU cr, cmpOpU cop) %{ predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_long() == 0); @@ -9741,7 +9699,6 @@ instruct cmovL_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src) %{ - predicate(!UseAPX); match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src)))); ins_cost(200); // XXX @@ -9761,29 +9718,15 @@ instruct cmovL_memUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, memory src) %{ %} %} -instruct cmovL_rReg_rReg_memU_ndd(rRegL dst, cmpOpU cop, rFlagsRegU cr, rRegL src1, memory src2) -%{ - predicate(UseAPX); - match(Set dst (CMoveL (Binary cop cr) (Binary src1 (LoadL src2)))); +instruct cmovL_memUCFE(cmpOpUCFE cop, rFlagsRegUCFE cr, rRegL dst, memory src) %{ + match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src)))); - ins_cost(200); - format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, long ndd" %} + ins_cost(200); // XXX + format %{ "cmovq$cop $dst, $src\t# unsigned, long" %} ins_encode %{ - __ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address); + __ cmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src$$Address); %} - ins_pipe(pipe_cmov_mem); -%} - -instruct cmovL_rReg_rReg_memUCFE_ndd(rRegL dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegL src1, memory src2) -%{ - match(Set dst (CMoveL (Binary cop cr) (Binary src1 (LoadL src2)))); - - ins_cost(200); - format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, long ndd" %} - ins_encode %{ - __ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address); - %} - ins_pipe(pipe_cmov_mem); + ins_pipe(pipe_cmov_mem); // XXX %} instruct cmovF_reg(cmpOp cop, rFlagsReg cr, regF dst, regF src) @@ -9970,23 +9913,8 @@ instruct addI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) ins_pipe( ialu_reg ); %} -instruct addI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AddI (LoadI src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); - - format %{ "eaddl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eaddl($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe( ialu_reg ); -%} - instruct addI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (AddI dst (LoadI src))); effect(KILL cr); flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); @@ -9999,21 +9927,6 @@ instruct addI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct addI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AddI src1 (LoadI src2))); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "eaddl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eaddl($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - instruct addI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) %{ match(Set dst (StoreI dst (AddI (LoadI dst) src))); @@ -10070,19 +9983,6 @@ instruct incI_rReg_ndd(rRegI dst, rRegI src, immI_1 val, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct incI_rReg_mem_ndd(rRegI dst, memory src, immI_1 val, rFlagsReg cr) -%{ - predicate(UseAPX && UseIncDec); - match(Set dst (AddI (LoadI src) val)); - effect(KILL cr); - - format %{ "eincl $dst, $src\t# int ndd" %} - ins_encode %{ - __ eincl($dst$$Register, $src$$Address, false); - %} - ins_pipe(ialu_reg); -%} - instruct incI_mem(memory dst, immI_1 src, rFlagsReg cr) %{ predicate(UseIncDec); @@ -10125,19 +10025,6 @@ instruct decI_rReg_ndd(rRegI dst, rRegI src, immI_M1 val, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct decI_rReg_mem_ndd(rRegI dst, memory src, immI_M1 val, rFlagsReg cr) -%{ - predicate(UseAPX && UseIncDec); - match(Set dst (AddI (LoadI src) val)); - effect(KILL cr); - - format %{ "edecl $dst, $src\t# int ndd" %} - ins_encode %{ - __ edecl($dst$$Register, $src$$Address, false); - %} - ins_pipe(ialu_reg); -%} - // XXX why does that use AddI instruct decI_mem(memory dst, immI_M1 src, rFlagsReg cr) %{ @@ -10260,23 +10147,8 @@ instruct addL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr ins_pipe( ialu_reg ); %} -instruct addL_rReg_mem_imm_ndd(rRegL dst, memory src1, immL32 src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AddL (LoadL src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); - - format %{ "eaddq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ eaddq($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe( ialu_reg ); -%} - instruct addL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (AddL dst (LoadL src))); effect(KILL cr); flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); @@ -10289,21 +10161,6 @@ instruct addL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct addL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AddL src1 (LoadL src2))); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "eaddq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ eaddq($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - instruct addL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) %{ match(Set dst (StoreL dst (AddL (LoadL dst) src))); @@ -10359,19 +10216,6 @@ instruct incL_rReg_ndd(rRegL dst, rRegI src, immL1 val, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct incL_rReg_mem_ndd(rRegL dst, memory src, immL1 val, rFlagsReg cr) -%{ - predicate(UseAPX && UseIncDec); - match(Set dst (AddL (LoadL src) val)); - effect(KILL cr); - - format %{ "eincq $dst, $src\t# long ndd" %} - ins_encode %{ - __ eincq($dst$$Register, $src$$Address, false); - %} - ins_pipe(ialu_reg); -%} - instruct incL_mem(memory dst, immL1 src, rFlagsReg cr) %{ predicate(UseIncDec); @@ -10414,19 +10258,6 @@ instruct decL_rReg_ndd(rRegL dst, rRegL src, immL_M1 val, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct decL_rReg_mem_ndd(rRegL dst, memory src, immL_M1 val, rFlagsReg cr) -%{ - predicate(UseAPX && UseIncDec); - match(Set dst (AddL (LoadL src) val)); - effect(KILL cr); - - format %{ "edecq $dst, $src\t# long ndd" %} - ins_encode %{ - __ edecq($dst$$Register, $src$$Address, false); - %} - ins_pipe(ialu_reg); -%} - // XXX why does that use AddL instruct decL_mem(memory dst, immL_M1 src, rFlagsReg cr) %{ @@ -11147,23 +10978,8 @@ instruct subI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) ins_pipe(ialu_reg_reg); %} -instruct subI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubI (LoadI src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); - - format %{ "esubl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ esubl($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg_reg); -%} - instruct subI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (SubI dst (LoadI src))); effect(KILL cr); flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); @@ -11176,36 +10992,6 @@ instruct subI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct subI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubI src1 (LoadI src2))); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); - - ins_cost(150); - format %{ "esubl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ esubl($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - -instruct subI_rReg_mem_rReg_ndd(rRegI dst, memory src1, rRegI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubI (LoadI src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); - - ins_cost(150); - format %{ "esubl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ esubl($dst$$Register, $src1$$Address, $src2$$Register, false); - %} - ins_pipe(ialu_reg_mem); -%} - instruct subI_mem_rReg(memory dst, rRegI src, rFlagsReg cr) %{ match(Set dst (StoreI dst (SubI (LoadI dst) src))); @@ -11262,23 +11048,8 @@ instruct subL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr ins_pipe(ialu_reg_reg); %} -instruct subL_rReg_mem_imm_ndd(rRegL dst, memory src1, immL32 src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubL (LoadL src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); - - format %{ "esubq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ esubq($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg_reg); -%} - instruct subL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (SubL dst (LoadL src))); effect(KILL cr); flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); @@ -11291,36 +11062,6 @@ instruct subL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct subL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubL src1 (LoadL src2))); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); - - ins_cost(150); - format %{ "esubq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ esubq($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - -instruct subL_rReg_mem_rReg_ndd(rRegL dst, memory src1, rRegL src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubL (LoadL src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag); - - ins_cost(150); - format %{ "esubq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ esubq($dst$$Register, $src1$$Address, $src2$$Register, false); - %} - ins_pipe(ialu_reg_mem); -%} - instruct subL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) %{ match(Set dst (StoreL dst (SubL (LoadL dst) src))); @@ -11535,7 +11276,6 @@ instruct mulI_rReg_imm(rRegI dst, rRegI src, immI imm, rFlagsReg cr) instruct mulI_mem(rRegI dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (MulI dst (LoadI src))); effect(KILL cr); @@ -11547,21 +11287,6 @@ instruct mulI_mem(rRegI dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem_alu0); %} -instruct mulI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (MulI src1 (LoadI src2))); - effect(KILL cr); - flag(PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(350); - format %{ "eimull $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eimull($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem_alu0); -%} - instruct mulI_mem_imm(rRegI dst, memory src, immI imm, rFlagsReg cr) %{ match(Set dst (MulI (LoadI src) imm)); @@ -11629,7 +11354,6 @@ instruct mulL_rReg_imm(rRegL dst, rRegL src, immL32 imm, rFlagsReg cr) instruct mulL_mem(rRegL dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (MulL dst (LoadL src))); effect(KILL cr); @@ -11641,20 +11365,6 @@ instruct mulL_mem(rRegL dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem_alu0); %} -instruct mulL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (MulL src1 (LoadL src2))); - effect(KILL cr); - flag(PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(350); - format %{ "eimulq $dst, $src1, $src2 \t# long" %} - ins_encode %{ - __ eimulq($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem_alu0); -%} instruct mulL_mem_imm(rRegL dst, memory src, immL32 imm, rFlagsReg cr) %{ @@ -11669,6 +11379,34 @@ instruct mulL_mem_imm(rRegL dst, memory src, immL32 imm, rFlagsReg cr) ins_pipe(ialu_reg_mem_alu0); %} +instruct mulHiLoL_rReg(rax_RegL rax, rdx_RegL rdx, rRegL src, rFlagsReg cr) +%{ + match(MulHiLoL src rax); + match(MulHiLoL rax src); + effect(KILL cr); + + ins_cost(300); + format %{ "imulq RDX:RAX, RAX, $src\t# mulhilo" %} + ins_encode %{ + __ imulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct umulHiLoL_rReg(rax_RegL rax, rdx_RegL rdx, rRegL src, rFlagsReg cr) +%{ + match(UMulHiLoL src rax); + match(UMulHiLoL rax src); + effect(KILL cr); + + ins_cost(300); + format %{ "mulq RDX:RAX, RAX, $src\t# umulhilo" %} + ins_encode %{ + __ mulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + instruct mulHiL_rReg(rdx_RegL dst, rRegL src, rax_RegL rax, rFlagsReg cr) %{ match(Set dst (MulHiL src rax)); @@ -11959,19 +11697,6 @@ instruct salI_rReg_imm_ndd(rRegI dst, rRegI src, immI8 shift, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct salI_rReg_mem_imm_ndd(rRegI dst, memory src, immI8 shift, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (LShiftI (LoadI src) shift)); - effect(KILL cr); - - format %{ "esall $dst, $src, $shift\t# int (ndd)" %} - ins_encode %{ - __ esall($dst$$Register, $src$$Address, $shift$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Shift Left by 8-bit immediate instruct salI_mem_imm(memory dst, immI8 shift, rFlagsReg cr) %{ @@ -12066,19 +11791,6 @@ instruct sarI_rReg_imm_ndd(rRegI dst, rRegI src, immI8 shift, rFlagsReg cr) ins_pipe(ialu_mem_imm); %} -instruct sarI_rReg_mem_imm_ndd(rRegI dst, memory src, immI8 shift, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (RShiftI (LoadI src) shift)); - effect(KILL cr); - - format %{ "esarl $dst, $src, $shift\t# int (ndd)" %} - ins_encode %{ - __ esarl($dst$$Register, $src$$Address, $shift$$constant, false); - %} - ins_pipe(ialu_mem_imm); -%} - // Arithmetic Shift Right by 8-bit immediate instruct sarI_mem_imm(memory dst, immI8 shift, rFlagsReg cr) %{ @@ -12173,19 +11885,6 @@ instruct shrI_rReg_imm_ndd(rRegI dst, rRegI src, immI8 shift, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct shrI_rReg_mem_imm_ndd(rRegI dst, memory src, immI8 shift, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (URShiftI (LoadI src) shift)); - effect(KILL cr); - - format %{ "eshrl $dst, $src, $shift\t # int (ndd)" %} - ins_encode %{ - __ eshrl($dst$$Register, $src$$Address, $shift$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Logical Shift Right by 8-bit immediate instruct shrI_mem_imm(memory dst, immI8 shift, rFlagsReg cr) %{ @@ -12310,19 +12009,6 @@ instruct salL_rReg_imm_ndd(rRegL dst, rRegL src, immI8 shift, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct salL_rReg_mem_imm_ndd(rRegL dst, memory src, immI8 shift, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (LShiftL (LoadL src) shift)); - effect(KILL cr); - - format %{ "esalq $dst, $src, $shift\t# long (ndd)" %} - ins_encode %{ - __ esalq($dst$$Register, $src$$Address, $shift$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Shift Left by 8-bit immediate instruct salL_mem_imm(memory dst, immI8 shift, rFlagsReg cr) %{ @@ -12417,19 +12103,6 @@ instruct sarL_rReg_imm_ndd(rRegL dst, rRegL src, immI shift, rFlagsReg cr) ins_pipe(ialu_mem_imm); %} -instruct sarL_rReg_mem_imm_ndd(rRegL dst, memory src, immI shift, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (RShiftL (LoadL src) shift)); - effect(KILL cr); - - format %{ "esarq $dst, $src, $shift\t# long (ndd)" %} - ins_encode %{ - __ esarq($dst$$Register, $src$$Address, (unsigned char)($shift$$constant & 0x3F), false); - %} - ins_pipe(ialu_mem_imm); -%} - // Arithmetic Shift Right by 8-bit immediate instruct sarL_mem_imm(memory dst, immI shift, rFlagsReg cr) %{ @@ -12524,19 +12197,6 @@ instruct shrL_rReg_imm_ndd(rRegL dst, rRegL src, immI8 shift, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct shrL_rReg_mem_imm_ndd(rRegL dst, memory src, immI8 shift, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (URShiftL (LoadL src) shift)); - effect(KILL cr); - - format %{ "eshrq $dst, $src, $shift\t# long (ndd)" %} - ins_encode %{ - __ eshrq($dst$$Register, $src$$Address, $shift$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Logical Shift Right by 8-bit immediate instruct shrL_mem_imm(memory dst, immI8 shift, rFlagsReg cr) %{ @@ -13064,24 +12724,9 @@ instruct andI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct andI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AndI (LoadI src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); - - format %{ "eandl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eandl($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // And Register with Memory instruct andI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (AndI dst (LoadI src))); effect(KILL cr); flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); @@ -13094,21 +12739,6 @@ instruct andI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct andI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AndI src1 (LoadI src2))); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "eandl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eandl($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - // And Memory with Register instruct andB_mem_rReg(memory dst, rRegI src, rFlagsReg cr) %{ @@ -13351,24 +12981,9 @@ instruct orI_rReg_imm_rReg_ndd(rRegI dst, immI src1, rRegI src2, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct orI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (OrI (LoadI src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); - - format %{ "eorl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eorl($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Or Register with Memory instruct orI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (OrI dst (LoadI src))); effect(KILL cr); flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); @@ -13381,21 +12996,6 @@ instruct orI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct orI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (OrI src1 (LoadI src2))); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "eorl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ eorl($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - // Or Memory with Register instruct orB_mem_rReg(memory dst, rRegI src, rFlagsReg cr) %{ @@ -13528,26 +13128,9 @@ instruct xorI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) ins_pipe(ialu_reg); %} -// Xor Memory with Immediate -instruct xorI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (XorI (LoadI src1) src2)); - effect(KILL cr); - ins_cost(150); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); - - format %{ "exorl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ exorl($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Xor Register with Memory instruct xorI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (XorI dst (LoadI src))); effect(KILL cr); flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); @@ -13560,21 +13143,6 @@ instruct xorI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct xorI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (XorI src1 (LoadI src2))); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "exorl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ exorl($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - // Xor Memory with Register instruct xorB_mem_rReg(memory dst, rRegI src, rFlagsReg cr) %{ @@ -13709,24 +13277,9 @@ instruct andL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr ins_pipe(ialu_reg); %} -instruct andL_rReg_mem_imm_ndd(rRegL dst, memory src1, immL32 src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AndL (LoadL src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); - - format %{ "eandq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ eandq($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // And Register with Memory instruct andL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (AndL dst (LoadL src))); effect(KILL cr); flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); @@ -13739,21 +13292,6 @@ instruct andL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct andL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (AndL src1 (LoadL src2))); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "eandq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ eandq($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - // And Memory with Register instruct andL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) %{ @@ -14027,25 +13565,9 @@ instruct orL_rReg_imm_rReg_ndd(rRegL dst, immL32 src1, rRegL src2, rFlagsReg cr) ins_pipe(ialu_reg); %} -// Or Memory with Immediate -instruct orL_rReg_mem_imm_ndd(rRegL dst, memory src1, immL32 src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (OrL (LoadL src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); - - format %{ "eorq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ eorq($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Or Register with Memory instruct orL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (OrL dst (LoadL src))); effect(KILL cr); flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); @@ -14058,21 +13580,6 @@ instruct orL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct orL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (OrL src1 (LoadL src2))); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "eorq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ eorq($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - // Or Memory with Register instruct orL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) %{ @@ -14208,26 +13715,9 @@ instruct xorL_rReg_rReg_imm(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr) ins_pipe(ialu_reg); %} -// Xor Memory with Immediate -instruct xorL_rReg_mem_imm(rRegL dst, memory src1, immL32 src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (XorL (LoadL src1) src2)); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); - ins_cost(150); - - format %{ "exorq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ exorq($dst$$Register, $src1$$Address, $src2$$constant, false); - %} - ins_pipe(ialu_reg); -%} - // Xor Register with Memory instruct xorL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) %{ - predicate(!UseAPX); match(Set dst (XorL dst (LoadL src))); effect(KILL cr); flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag); @@ -14240,21 +13730,6 @@ instruct xorL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) ins_pipe(ialu_reg_mem); %} -instruct xorL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (XorL src1 (LoadL src2))); - effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); - - ins_cost(150); - format %{ "exorq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ exorq($dst$$Register, $src1$$Register, $src2$$Address, false); - %} - ins_pipe(ialu_reg_mem); -%} - // Xor Memory with Register instruct xorL_mem_rReg(memory dst, rRegL src, rFlagsReg cr) %{ diff --git a/src/hotspot/cpu/zero/frame_zero.hpp b/src/hotspot/cpu/zero/frame_zero.hpp index af9abe22cbe..514b134e36b 100644 --- a/src/hotspot/cpu/zero/frame_zero.hpp +++ b/src/hotspot/cpu/zero/frame_zero.hpp @@ -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. * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,6 @@ public: enum { - pc_return_offset = 0, metadata_words = 0, // size, in words, of metadata at frame bottom, i.e. it is not part of the // caller/callee overlap @@ -39,6 +38,7 @@ // between a callee frame and its stack arguments, where it is part // of the caller/callee overlap metadata_words_at_top = 0, + // in bytes frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 @@ -82,8 +82,6 @@ char* buf, int buflen) const; - static jint interpreter_frame_expression_stack_direction() { return -1; } - inline address* sender_pc_addr() const; template diff --git a/src/hotspot/os/linux/globals_linux.hpp b/src/hotspot/os/linux/globals_linux.hpp index 90e1e5e5f3f..fa7b5a63c6c 100644 --- a/src/hotspot/os/linux/globals_linux.hpp +++ b/src/hotspot/os/linux/globals_linux.hpp @@ -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 @@ -94,6 +94,10 @@ " 0 = no timeout (default)") \ range(0,1000000) \ \ + product(ccstr, AltTempDir, nullptr, \ + "Alternate temporary directory for JVM files.") \ + \ + // end of RUNTIME_OS_FLAGS // diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index ad1f384fa32..12a4ea2bda4 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -112,6 +112,7 @@ # include # include # include +# include # include # include # include @@ -1547,11 +1548,47 @@ int os::current_process_id() { return ::getpid(); } -// DLL functions +static bool is_writable_directory(const char* name) { + struct stat mystat; + int ret_val = stat(name, &mystat); + return (ret_val != -1 && S_ISDIR(mystat.st_mode) > 0 && access(name, R_OK|W_OK|X_OK) == 0); +} -// This must be hard coded because it's the system's temporary -// directory not the java application's temp directory, ala java.io.tmpdir. -const char* os::get_temp_directory() { return "/tmp"; } +// Check that a given alternate temporary directory name specifies an absolute path and is an existing, writable +// directory. + +// If it is not an absolute path, revert back to hardcoded /tmp. If the directory is non existant or not +// writable give a warning but use AltTempDir. In the latter case, we may be connecting to a process that is +// inside a container. +// +// Since the attach mechanism uses the socket name length, this limits the length of the alternate +// temporary directory name. We don't check that here since the temporary directory is +// used for many things. The perfData and attach code will check it. + +void os::pd_check_temp_directory() { + if (AltTempDir != nullptr && AltTempDir[0] != '\0') { + if (AltTempDir[0] != '/') { + log_warning(os)("Warning: AltTempDir is ignored because it must be an absolute pathname"); + AltTempDir = nullptr; + } else { + if (!is_writable_directory(AltTempDir)) { + // This is only a warning and still uses AltTempDir, which is needed to attach to a + // containerized process from the host. + log_warning(os)("Warning: AltTempDir is not an existing or writable directory"); + } + } + } else { + if (!is_writable_directory("/tmp")) { + log_warning(os)("Warning: /tmp is not writable. Consider using -XX:AltTempDir=/ to set a writable temp directory"); + } + AltTempDir = nullptr; // avoid checking AltTempDir[0] again. + } +} + +const char* os::get_temp_directory() { + // AltTempDir is already checked. + return AltTempDir != nullptr ? AltTempDir : "/tmp"; +} // check if addr is inside libjvm.so bool os::address_is_in_vm(address addr) { @@ -4662,20 +4699,6 @@ void os::Linux::numa_init() { if (UseNUMA && !UseNUMAInterleaving) { FLAG_SET_ERGO_IF_DEFAULT(UseNUMAInterleaving, true); } - -#if INCLUDE_PARALLELGC - if (UseParallelGC && UseNUMA && UseLargePages && !can_commit_large_page_memory()) { - // With static large pages we cannot uncommit a page, so there's no way - // we can make the adaptive lgrp chunk resizing work. If the user specified both - // UseNUMA and UseLargePages on the command line - warn and disable adaptive resizing. - if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) { - warning("UseNUMA is not fully compatible with +UseLargePages, " - "disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)"); - UseAdaptiveSizePolicy = false; - UseAdaptiveNUMAChunkSizing = false; - } - } -#endif } void os::Linux::disable_numa(const char* reason, bool warning) { diff --git a/src/hotspot/os/posix/attachListener_posix.cpp b/src/hotspot/os/posix/attachListener_posix.cpp index a7cf1703128..152fd6140e0 100644 --- a/src/hotspot/os/posix/attachListener_posix.cpp +++ b/src/hotspot/os/posix/attachListener_posix.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 @@ -201,6 +201,8 @@ int PosixAttachListener::init() { n = os::snprintf(initial_path, UNIX_PATH_MAX, "%s.tmp", path); } if (n >= (int)UNIX_PATH_MAX) { + log_warning(attach)("Failed to create temporary file for attach %s/.java_pid%d: file name is too long", + os::get_temp_directory(), os::current_process_id()); return -1; } @@ -346,8 +348,11 @@ void AttachListener::vm_start() { struct stat st; int ret; - os::snprintf_checked(fn, UNIX_PATH_MAX, "%s/.java_pid%d", + int n = os::snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", os::get_temp_directory(), os::current_process_id()); + if (n >= (int)UNIX_PATH_MAX) { + return; + } RESTARTABLE(::stat(fn, &st), ret); if (ret == 0) { diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 300c86ffc47..aaeb33b6d9b 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -135,23 +135,25 @@ static void save_memory_to_file(char* addr, size_t size) { // return the user specific temporary directory name. // the caller is expected to free the allocated memory. // -#define TMP_BUFFER_LEN (4+22) static char* get_user_tmp_dir(const char* user, int vmid, int nspid) { char* tmpdir = (char *)os::get_temp_directory(); + char buffer[PATH_MAX] = {0}; #if defined(LINUX) // On linux, if containerized process, get dirname of // /proc/{vmid}/root/tmp/{PERFDATA_NAME_user} // otherwise /tmp/{PERFDATA_NAME_user} - char buffer[TMP_BUFFER_LEN]; - assert(strlen(tmpdir) == 4, "No longer using /tmp - update buffer size"); + // The /tmp directory can be overridden with AltTempDir. if (nspid != -1) { - jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir); + int val = os::snprintf(buffer, PATH_MAX, "/proc/%d/root%s", vmid, tmpdir); + if (val >= (int)PATH_MAX) { + log_warning(perf)("The temporary directory for perf data /proc/%d/root%s name is truncated", + vmid, tmpdir); + } tmpdir = buffer; } #endif #ifdef __APPLE__ - char buffer[PATH_MAX] = {0}; // Check if the current user is root and the target VM is running as non-root. // Otherwise the output of os::get_temp_directory() is used. // @@ -524,7 +526,6 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { char* tmpdirname = (char *)os::get_temp_directory(); #if defined(LINUX) char buffer[MAXPATHLEN + 1]; - assert(strlen(tmpdirname) == 4, "No longer using /tmp - update buffer size"); // On Linux, if nspid != -1, look in /proc/{vmid}/root/tmp for directories // containing nspid, otherwise just look for vmid in /tmp. diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 203e13a46ac..bdce5c40feb 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -1881,6 +1881,12 @@ void PosixSignals::do_resume(OSThread* osthread) { } void SuspendedThreadTask::internal_do_task() { +#if INCLUDE_JFR + assert(NOT_COMPILER2(true) COMPILER2_PRESENT(!HotCodeHeap) || + SuspendedThreadTask_lock->owned_by_self(), + "suspend/resume must be serialized when HotCodeHeap is enabled"); +#endif + if (PosixSignals::do_suspend(_thread->osthread())) { SuspendedThreadTaskContext context(_thread, _thread->osthread()->ucontext()); do_task(context); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index d00babef40f..f62e9c298e8 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3507,6 +3507,152 @@ char* os::pd_reserve_memory(size_t bytes, bool exec) { return pd_attempt_reserve_memory_at(nullptr /* addr */, bytes, exec); } +// This allocates a placeholder via VirtualAlloc2(MEM_RESERVE_PLACEHOLDER). +os::win32::PlaceholderRegion os::win32::reserve_placeholder_memory(size_t bytes, char* addr) { + assert(bytes > 0, "Size must be a value greater than 0"); + assert(is_aligned(addr, os::vm_allocation_granularity()), "Requested address should be aligned to allocation granularity."); + assert(is_aligned(bytes, os::vm_page_size()), "Requested size, bytes, should be aligned to page size."); + + if (!is_VirtualAlloc2_supported()) { + return PlaceholderRegion(); + } + + char* res = (char*)os::win32::VirtualAlloc2( + GetCurrentProcess(), + addr, + bytes, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, + nullptr, 0); + + if (res != nullptr) { + log_trace(os)("VirtualAlloc2 placeholder of size (%zu) returned " PTR_FORMAT ".", bytes, p2i(res)); + return PlaceholderRegion(res, bytes); + } else { + log_warning(os)("VirtualAlloc2 placeholder reservation of size (%zu) at " PTR_FORMAT ": error %lu.", bytes, p2i(addr), GetLastError()); + return PlaceholderRegion(); + } +} + +os::win32::PlaceholderRegionPair os::win32::split_memory(const PlaceholderRegion& orig, size_t offset) { + guarantee(is_VirtualAlloc2_supported(), "split_memory requires VirtualAlloc2."); + assert(!orig.is_empty(), "Region cannot be empty"); + assert(offset <= orig.size(), "Offset must be less than or equal to region size"); + + char* original_base = orig.base(); + size_t original_size = orig.size(); + + if (offset == 0) { + log_trace(os)("Split memory has offset 0: " RANGEFMT, RANGEFMTARGS(original_base, original_size)); + return { PlaceholderRegion(), orig }; + } else if (offset == original_size) { + log_trace(os)("Split memory consumed the whole region: " RANGEFMT, RANGEFMTARGS(original_base, original_size)); + return { orig, PlaceholderRegion() }; + } + + assert(is_aligned(offset, os::vm_allocation_granularity()), "If the split does not consume the entire original region, the offset should be aligned to allocation granularity since a new Placeholder is spawned the split point."); + + // VirtualFree with MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER splits the + // placeholder [original_base, original_base+original_size) in two: + // [original_base, original_base+offset) and [original_base+offset, original_base+original_size) + // + // With correct inputs, this should not fail. + // A failure indicates either a programming error (e.g., bad alignment, + // region not actually a placeholder) or a catastrophic system problem. + // Crashing with a diagnostic is more useful than attempting recovery. + BOOL result = virtualFree(original_base, offset, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + guarantee(result != FALSE, + "Failed to split placeholder at " PTR_FORMAT " (offset %zu): error %lu.", + p2i(original_base), offset, GetLastError()); + + log_trace(os)("Split placeholder " RANGE_FORMAT " at offset %zu.", + RANGE_FORMAT_ARGS(original_base, original_size), offset); + + return {PlaceholderRegion(original_base, offset), PlaceholderRegion(original_base + offset, original_size - offset)}; +} + +char* os::win32::convert_to_reserved(PlaceholderRegion region, int numa_node) { + guarantee(is_VirtualAlloc2_supported(), "convert_to_reserved requires VirtualAlloc2"); + assert(!region.is_empty(), "Region cannot be empty"); + + char* base = region.base(); + size_t size = region.size(); + + assert(base != nullptr, "Region base cannot be null"); + assert(size > 0, "Region size must be positive"); + + MEM_EXTENDED_PARAMETER param = { 0 }; + MEM_EXTENDED_PARAMETER* param_ptr = nullptr; + ULONG param_count = 0; + + if (numa_node >= 0) { + param.Type = MemExtendedParameterNumaNode; + param.ULong = (DWORD)numa_node; + param_ptr = ¶m; + param_count = 1; + } + + // Similar to split_memory, with correct inputs, this should never fail. + char* reserved = (char*)os::win32::VirtualAlloc2( + GetCurrentProcess(), + base, + size, + MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, + param_ptr, param_count); + guarantee(reserved != nullptr, + "Failed to convert placeholder to reservation at " PTR_FORMAT " (%zu, numa node %d): error %lu.", + p2i(base), size, numa_node, GetLastError()); + + if (numa_node >= 0) { + log_trace(os)("Converted placeholder " RANGE_FORMAT " to reservation on NUMA node %d.", RANGE_FORMAT_ARGS(reserved, size), numa_node); + } else { + log_trace(os)("Converted placeholder " RANGE_FORMAT " to reservation.", RANGE_FORMAT_ARGS(reserved, size)); + } + + return reserved; +} + +// Reserve a region split across NUMA nodes. +// Uses VirtualAlloc2 placeholders in order to avoid races when splitting up the initial reservation into +// chunks assigned to different nodes. Returns the base address of the reserved range, or nullptr on failure. +static char* reserve_with_numa_placeholder(char* addr, size_t bytes) { + assert(is_VirtualAlloc2_supported(), "requires VirtualAlloc2"); + + const size_t chunk_size = NUMAInterleaveGranularity; + + // Reserve the full range as a placeholder. + // If we requested an address, reserve_placeholder_memory will obtain it or fail. + os::win32::PlaceholderRegion whole_range = os::win32::reserve_placeholder_memory(bytes, addr); + if (whole_range.is_empty()) { + log_warning(os)("Failed to reserve placeholder for NUMA interleaving (" PTR_FORMAT ", %zu).", p2i(addr), bytes); + return nullptr; + } + + char* const whole_range_base = whole_range.base(); + log_trace(os)("Created VirtualAlloc2 NUMA placeholder at " RANGE_FORMAT " (%zu bytes).", RANGE_FORMAT_ARGS(whole_range_base, bytes), bytes); + + char* cur = whole_range_base; + size_t remaining_len = whole_range.size(); + + int count = 0; + const int node_count = numa_node_list_holder.get_count(); + + while (remaining_len > 0) { + const size_t bytes_to_rq = MIN2(remaining_len, chunk_size - ((uintptr_t)cur % chunk_size)); + os::win32::PlaceholderRegion remaining(cur, remaining_len); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(remaining, bytes_to_rq); + // Assign 0 for testing on systems without NUMA interleaving + DWORD node = node_count > 0 ? numa_node_list_holder.get_node_list_entry(count % node_count) : 0; + os::win32::convert_to_reserved(split.left, (int)node); + cur = split.right.base(); + remaining_len = split.right.size(); + count++; + } + + return whole_range_base; +} + // Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes, bool exec) { @@ -3516,23 +3662,32 @@ char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes, bool exec) { char* res; // note that if UseLargePages is on, all the areas that require interleaving // will go thru reserve_memory_special rather than thru here. - bool use_individual = (UseNUMAInterleaving && !UseLargePages); - if (!use_individual) { - res = (char*)virtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE); - } else { + bool use_numa_interleaving = (UseNUMAInterleaving && !UseLargePages); + if (use_numa_interleaving) { elapsedTimer reserveTimer; if (Verbose && PrintMiscellaneous) reserveTimer.start(); - // in numa interleaving, we have to allocate pages individually - // (well really chunks of NUMAInterleaveGranularity size) - res = allocate_pages_individually(bytes, addr, MEM_RESERVE, PAGE_READWRITE); - if (res == nullptr) { - warning("NUMA page allocation failed"); + if (is_VirtualAlloc2_supported()) { + // Splittable NUMA interleaving with VirtualAlloc2 placeholders. + res = reserve_with_numa_placeholder(addr, bytes); + if (res == nullptr) { + log_warning(os)("NUMA allocation using placeholders failed"); + } + } else { + // Non-splittable NUMA interleaving: allocate_pages_individually (possible races). + // (well really chunks of NUMAInterleaveGranularity size) + res = allocate_pages_individually(bytes, addr, MEM_RESERVE, PAGE_READWRITE); + if (res == nullptr) { + log_warning(os)("NUMA page allocation failed"); + } } if (Verbose && PrintMiscellaneous) { reserveTimer.stop(); tty->print_cr("reserve_memory of %zx bytes took " JLONG_FORMAT " ms (" JLONG_FORMAT " ticks)", bytes, - reserveTimer.milliseconds(), reserveTimer.ticks()); + reserveTimer.milliseconds(), reserveTimer.ticks()); } + } else { + // Standard reservation. + res = (char*)virtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE); } assert(res == nullptr || addr == nullptr || addr == res, "Unexpected address from reserve."); @@ -6058,6 +6213,11 @@ static inline HANDLE get_thread_handle_for_extended_context(DWORD tid) { // Thread sampling implementation // void SuspendedThreadTask::internal_do_task() { +#if INCLUDE_JFR + assert(NOT_COMPILER2(true) COMPILER2_PRESENT(!HotCodeHeap) || + SuspendedThreadTask_lock->owned_by_self(), + "suspend/resume must be serialized when HotCodeHeap is enabled"); +#endif const HANDLE h = get_thread_handle_for_extended_context(_thread->osthread()->thread_id()); if (h == nullptr) { return; @@ -6382,7 +6542,7 @@ void os::jfr_report_memory_info() { // Send the RSS JFR event EventResidentSetSize event; event.set_size(pmex.WorkingSetSize); - event.set_peak(pmex.PeakWorkingSetSize); + event.set_peak(MAX2(pmex.PeakWorkingSetSize, pmex.WorkingSetSize)); event.commit(); } else { // Log a warning diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index 5ebc80c817b..68e77c9957f 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -122,6 +122,57 @@ class os::win32 { typedef PVOID (WINAPI *MapViewOfFile3Fn)(HANDLE, HANDLE, PVOID, ULONG64, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); static MapViewOfFile3Fn MapViewOfFile3; + // A "reserved" region of address space that can be split or converted to a + // normal reservation. Conceptually distinct from a reserved region: + // callers must NOT call commit_memory, map_memory, or other operations + // directly on the raw address. They must first convert it via + // convert_to_reserved(). + class PlaceholderRegion { + char* const _base; + size_t const _size; + public: + PlaceholderRegion() : _base(nullptr), _size(0) {} + PlaceholderRegion(char* base, size_t size) : _base(base), _size(size) { + if (base != nullptr) { + assert(size > 0, "Non-empty Placeholder must have positive size."); + assert(is_aligned(base, os::vm_allocation_granularity()), "New Placeholder base should be aligned to allocation granularity."); + assert(is_aligned(size, os::vm_page_size()), "New Placeholder size should be page-aligned"); + } else { + assert(size == 0, "Empty Placeholder must have zero size."); + } + } + PlaceholderRegion(const PlaceholderRegion& source) : PlaceholderRegion(source._base, source._size) {} + char* base() const { return _base; } + size_t size() const { return _size; } + bool is_empty() const { return _base == nullptr; } + }; + + struct PlaceholderRegionPair { + PlaceholderRegion left; + PlaceholderRegion right; + }; + + // Reserves a virtual memory region that can be split after allocation. + // The returned region must be converted via convert_to_reserved() before committing. + // If the returned PlaceholderRegion is empty, the reservation failed. + // This should only be called after os::init_2() has completed, otherwise the Windows API may not be initialized. + // Uses VirtualAlloc2, which requires the base address be null or aligned to allocation granularity. + static PlaceholderRegion reserve_placeholder_memory(size_t bytes, char* addr); + + // Split 'orig' at 'offset'. Returns left and right placeholder pieces as a PlaceholderRegionPair. + // The caller must not use 'orig' afterward. + // Offset must be aligned to allocation granularity. + // If offset == orig.size(), returns { orig, empty }. + // If offset == 0, returns { empty, orig }. + // This should not fail. If unsuccessful, this function fails fatally. + static PlaceholderRegionPair split_memory(const PlaceholderRegion& orig, size_t offset); + + // Convert a placeholder region into a regular reserved region via VirtualAlloc2(MEM_REPLACE_PLACEHOLDER). + // After conversion the Placeholder region should no longer be used. + // This should not fail. If unsuccessful, this function fails fatally. + // If numa_node >= 0, binds the reservation to that NUMA node. + static char* convert_to_reserved(PlaceholderRegion region, int numa_node = -1); + private: static void initialize_performance_counter(); diff --git a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp index 6854fb805a9..51851993730 100644 --- a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp +++ b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. 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 @@ -509,7 +509,13 @@ void os::print_register_info(outputStream *st, const void *context, int& continu extern "C" { int SpinPause() { - return 0; + // Setting prio low, then prio medium results in a pseudo yield. + // Yield (or 27,27,27) was never implemented on PPC. + // or 1,1,1 = smt_prio_low + // or 2,2,2 = smt_prio_medium + asm volatile ("or 1,1,1\n\t" + "or 2,2,2" : : : "memory"); + return 1; } } diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index a3bd1bfa870..bbefd4ba50d 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -215,11 +215,9 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBS)) { VM_Version::ext_Zbs.enable_feature(); } -#ifndef PRODUCT if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFA)) { VM_Version::ext_Zfa.enable_feature(); } -#endif if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFH)) { VM_Version::ext_Zfh.enable_feature(); } @@ -239,16 +237,19 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZTSO)) { VM_Version::ext_Ztso.enable_feature(); } - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBB)) { - VM_Version::ext_Zvbb.enable_feature(); - } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBC)) { VM_Version::ext_Zvbc.enable_feature(); } #endif + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBB)) { + VM_Version::ext_Zvbb.enable_feature(); + } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { VM_Version::ext_Zvfh.enable_feature(); } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFHMIN)) { + VM_Version::ext_Zvfhmin.enable_feature(); + } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNED) && is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNHB) && is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKB) && @@ -261,13 +262,16 @@ void RiscvHwprobe::add_features_from_query_result() { // ====== non-extensions ====== // - if (is_valid(RISCV_HWPROBE_KEY_MARCHID)) { + // For value-type keys, the kernel returns (uint64_t)-1 when CPUs in the + // query set disagree (different core types). Skip these as the value is + // not meaningful for the system as a whole. + if (is_valid(RISCV_HWPROBE_KEY_MARCHID) && query[RISCV_HWPROBE_KEY_MARCHID].value != (uint64_t)-1) { VM_Version::marchid.enable_feature(query[RISCV_HWPROBE_KEY_MARCHID].value); } - if (is_valid(RISCV_HWPROBE_KEY_MIMPID)) { + if (is_valid(RISCV_HWPROBE_KEY_MIMPID) && query[RISCV_HWPROBE_KEY_MIMPID].value != (uint64_t)-1) { VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value); } - if (is_valid(RISCV_HWPROBE_KEY_MVENDORID)) { + if (is_valid(RISCV_HWPROBE_KEY_MVENDORID) && query[RISCV_HWPROBE_KEY_MVENDORID].value != (uint64_t)-1) { VM_Version::mvendorid.enable_feature(query[RISCV_HWPROBE_KEY_MVENDORID].value); } // RISCV_HWPROBE_KEY_CPUPERF_0 is deprecated and returns similar values diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index 648131b94a3..c9556d32cc5 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -307,7 +307,6 @@ void VM_Version::rivos_features() { ext_Zfh.enable_feature(); - ext_Zicboz.enable_feature(); ext_Zicsr.enable_feature(); ext_Zifencei.enable_feature(); ext_Zic64b.enable_feature(); diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 6750b71476b..25ee449d8b1 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -381,9 +381,22 @@ size_t os::Posix::default_stack_size(os::ThreadType thr_type) { ///////////////////////////////////////////////////////////////////////////// // helper functions for fatal error handler +// XSAVE Buffer Layout (Intel SDM Vol. 1, Section 13.4.1) +// Bytes 0-511: Legacy x87/FPU and SSE state (includes XMM0-15) +// Bytes 512-575: XSAVE Header (64 bytes) +// Bytes 576-831: YMMH state (upper 128 bits of YMM0-15) +// YMMH[i] at: buffer + 576 + (i * 16) +// Bytes 832+: Extended state components (e.g., AVX-512, APX, etc.). +// Component offsets and sizes are +// enumerated by CPUID.(EAX=0xD, ECX=n). // XSAVE constants - from Intel SDM Vol. 1, Chapter 13 #define XSAVE_HDR_OFFSET 512 +#define XSAVE_HDR_SIZE 64 #define XFEATURE_APX (1ULL << 19) +#define XFEATURE_YMM (1ULL << 2) +#define XFEATURE_OPMASK (1ULL << 5) +#define XFEATURE_ZMM_HI256 (1ULL << 6) +#define XFEATURE_HI16_ZMM (1ULL << 7) // XSAVE header structure // See: Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header" @@ -417,6 +430,118 @@ static apx_state* get_apx_state(const ucontext_t* uc) { return (apx_state*)(xsave + offset); } +static void print_xmm_registers(outputStream* st, const ucontext_t* uc) { + for (int i = 0; i < 16; ++i) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm[1], xmm[0]); + } +} + +static void print_ymm_registers(outputStream* st, const ucontext_t* uc, bool has_ymm_hi128) { + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + for (int i = 0; i < 16; ++i) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + uint64_t values[4] = {xmm[0], xmm[1], 0, 0}; + if (has_ymm_hi128) { + const uint64_t* ymmh = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET + XSAVE_HDR_SIZE + (i * 16)); + values[2] = ymmh[0]; + values[3] = ymmh[1]; + } + st->print("YMM[%d]=", i); + for (int j = 3; j >= 0; --j) { + st->print("%s" INTPTR_FORMAT, (j == 3) ? "" : " ", values[j]); + } + st->cr(); + } +} + +static void print_zmm_registers(outputStream* st, const ucontext_t* uc, bool has_ymm_hi128, + bool has_zmm_hi256, bool has_hi16_zmm) { + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + + for (int i = 0; i < 32; ++i) { + uint64_t values[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + if (i < 16) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + values[0] = xmm[0]; + values[1] = xmm[1]; + + if (has_ymm_hi128) { + const uint64_t* ymmh = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET + XSAVE_HDR_SIZE + (i * 16)); + values[2] = ymmh[0]; + values[3] = ymmh[1]; + } + + if (has_zmm_hi256) { + const uint32_t zmm_hi256_offset = VM_Version::zmm0to15_hi256_xstate_offset(); + const uint64_t* zmm_hi256 = (const uint64_t*)(xsave + zmm_hi256_offset + (i * 32)); + values[4] = zmm_hi256[0]; + values[5] = zmm_hi256[1]; + values[6] = zmm_hi256[2]; + values[7] = zmm_hi256[3]; + } + } else if (has_hi16_zmm) { + const uint32_t hi16_zmm_offset = VM_Version::zmm16to31_xstate_offset(); + const uint64_t* zmm = (const uint64_t*)(xsave + hi16_zmm_offset + ((i - 16) * 64)); + values[0] = zmm[0]; + values[1] = zmm[1]; + values[2] = zmm[2]; + values[3] = zmm[3]; + values[4] = zmm[4]; + values[5] = zmm[5]; + values[6] = zmm[6]; + values[7] = zmm[7]; + } + + st->print("ZMM[%d]=", i); + for (int j = 7; j >= 0; --j) { + st->print("%s" INTPTR_FORMAT, (j == 7) ? "" : " ", values[j]); + } + st->cr(); + } +} + +static void print_kmask_registers(outputStream* st, const ucontext_t* uc, bool has_opmask) { + const uint32_t opmask_offset = VM_Version::opmask_xstate_offset(); + if (!has_opmask || opmask_offset == 0) { + return; + } + + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + const uint64_t* kmask = (const uint64_t*)(xsave + opmask_offset); + + for (int i = 0; i < 8; ++i) { + st->print_cr("K[%d]=" INTPTR_FORMAT, i, kmask[i]); + } + st->cr(); +} + +static void print_vector_registers(outputStream* st, const ucontext_t* uc) { + if (uc->uc_mcontext.fpregs == nullptr) { + return; + } + + if (UseAVX < 2) { + return print_xmm_registers(st, uc); + } + + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + const uint64_t* xstate_hdr_ptr = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET); + const uint64_t xsave_state_bitmap = xstate_hdr_ptr[0]; + const bool has_ymm_hi128 = (xsave_state_bitmap & XFEATURE_YMM) != 0; + const bool has_opmask = (xsave_state_bitmap & XFEATURE_OPMASK) != 0; + const bool has_zmm_hi256 = (xsave_state_bitmap & XFEATURE_ZMM_HI256) != 0; + const bool has_hi16_zmm = (xsave_state_bitmap & XFEATURE_HI16_ZMM) != 0; + const bool should_print_zmm_registers = (UseAVX > 2) && (has_zmm_hi256 || has_hi16_zmm); + + if (!should_print_zmm_registers) { + return print_ymm_registers(st, uc, has_ymm_hi128); + } + + print_kmask_registers(st, uc, has_opmask); + print_zmm_registers(st, uc, has_ymm_hi128, has_zmm_hi256, has_hi16_zmm); +} void os::print_context(outputStream *st, const void *context) { if (context == nullptr) return; @@ -458,7 +583,7 @@ void os::print_context(outputStream *st, const void *context) { st->print(", ERR=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_ERR]); st->cr(); st->print(" TRAPNO=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_TRAPNO]); - // Add XMM registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. + // Add vector registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. st->cr(); st->cr(); // Sanity check: fpregs should point into the context. @@ -467,10 +592,7 @@ void os::print_context(outputStream *st, const void *context) { st->print_cr("bad uc->uc_mcontext.fpregs: " INTPTR_FORMAT " (uc: " INTPTR_FORMAT ")", p2i(uc->uc_mcontext.fpregs), p2i(uc)); } else { - for (int i = 0; i < 16; ++i) { - const int64_t* xmm_val_addr = (int64_t*)&(uc->uc_mcontext.fpregs->_xmm[i]); - st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm_val_addr[1], xmm_val_addr[0]); - } + print_vector_registers(st, uc); st->print(" MXCSR=" UINT32_FORMAT_X_0, uc->uc_mcontext.fpregs->mxcsr); } st->cr(); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 76634fc3fba..8106258c331 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -164,18 +164,16 @@ size_t AOTMetaspace::protection_zone_size() { return os::cds_core_region_alignment(); } -static bool shared_base_valid(char* shared_base) { - // We check user input for SharedBaseAddress at dump time. - +bool AOTMetaspace::shared_base_valid(char* shared_base) { // At CDS runtime, "shared_base" will be the (attempted) mapping start. It will also // be the encoding base, since the headers of archived base objects (and with Lilliput, // the prototype mark words) carry pre-computed narrow Klass IDs that refer to the mapping // start as base. - // - // On AARCH64, The "shared_base" may not be later usable as encoding base, depending on the - // total size of the reserved area and the precomputed_narrow_klass_shift. This is checked - // before reserving memory. Here we weed out values already known to be invalid later. - return AARCH64_ONLY(is_aligned(shared_base, 4 * G)) NOT_AARCH64(true); + // Note that all narrowKlass inside CDS/AOT archives will be precomputed with the + // shift that, at build time, will afford us the maximum encoding range of 4GB. We do this + // since we don't know how large the class space at runtime will actually be. + return CLASS_SPACE_ONLY(is_aligned(shared_base, Metaspace::reserve_alignment())) + NOT_CLASS_SPACE(true); } class DumpClassListCLDClosure : public CLDClosure { @@ -273,7 +271,7 @@ static char* compute_shared_base(size_t cds_max) { err = "too high"; } else if (shared_base_too_high(specified_base, aligned_base, cds_max)) { err = "too high"; - } else if (!shared_base_valid(aligned_base)) { + } else if (!AOTMetaspace::shared_base_valid(aligned_base)) { err = "invalid for this platform"; } else { return aligned_base; @@ -291,7 +289,7 @@ static char* compute_shared_base(size_t cds_max) { // Make sure the default value of SharedBaseAddress specified in globals.hpp is sane. assert(!shared_base_too_high(specified_base, aligned_base, cds_max), "Sanity"); - assert(shared_base_valid(aligned_base), "Sanity"); + assert(AOTMetaspace::shared_base_valid(aligned_base), "Sanity"); return aligned_base; } @@ -1176,20 +1174,6 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS AOTReferenceObjSupport::initialize(CHECK); AOTReferenceObjSupport::stabilize_cached_reference_objects(CHECK); - - if (CDSConfig::is_dumping_aot_linked_classes()) { - // java.lang.Class::reflectionFactory cannot be archived yet. We set this field - // to null, and it will be initialized again at runtime. - log_debug(aot)("Resetting Class::reflectionFactory"); - TempNewSymbol method_name = SymbolTable::new_symbol("resetArchivedStates"); - Symbol* method_sig = vmSymbols::void_method_signature(); - JavaValue result(T_VOID); - JavaCalls::call_static(&result, vmClasses::Class_klass(), - method_name, method_sig, CHECK); - - // Perhaps there is a way to avoid hard-coding these names here. - // See discussion in JDK-8342481. - } } else { log_info(aot)("Not dumping heap, reset CDSConfig::_is_using_optimized_module_handling"); CDSConfig::stop_using_optimized_module_handling(); @@ -1985,18 +1969,11 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin const size_t total_range_size = archive_space_size + gap_size + class_space_size; - // Test that class space base address plus shift can be decoded by aarch64, when restored. - const int precomputed_narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); - if (!CompressedKlassPointers::check_klass_decode_mode(base_address, precomputed_narrow_klass_shift, - total_range_size)) { - aot_log_info(aot)("CDS initialization: Cannot use SharedBaseAddress " PTR_FORMAT " with precomputed shift %d.", - p2i(base_address), precomputed_narrow_klass_shift); - use_archive_base_addr = false; - } - assert(total_range_size > ccs_begin_offset, "must be"); if (use_windows_memory_mapping() && use_archive_base_addr) { if (base_address != nullptr) { + // Note: We already checked the base address for validity at dump time. + // On Windows, we cannot safely split a reserved memory space into two (see JDK-8255917). // Hence, we optimistically reserve archive space and class space side-by-side. We only // do this for use_archive_base_addr=true since for use_archive_base_addr=false case diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 975b6be76d7..cc90c9da3b0 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -188,6 +188,9 @@ public: static bool use_optimized_module_handling() { return NOT_CDS(false) CDS_ONLY(_use_optimized_module_handling); } static void disable_optimized_module_handling() { _use_optimized_module_handling = false; } + // Check if the supplied shared base address can be used as the encoding base. + static bool shared_base_valid(char* shared_base); + private: static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN; static void fork_and_dump_final_static_archive(TRAPS); diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index 2d5fc8c7f21..62ed3a56b62 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -142,6 +142,16 @@ void AOTReferenceObjSupport::stabilize_cached_reference_objects(TRAPS) { vmSymbols::void_method_signature(), CHECK); } + { + TempNewSymbol method_name = SymbolTable::new_symbol("assemblySetup"); + JavaValue result(T_VOID); + Symbol* baseLocale_name = vmSymbols::sun_util_locale_BaseLocale(); + Klass* baseLocale_klass = SystemDictionary::resolve_or_fail(baseLocale_name, true, CHECK); + JavaCalls::call_static(&result, baseLocale_klass, + method_name, + vmSymbols::void_method_signature(), + CHECK); + } { Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 2dd1d9d0824..63d1f4af4ae 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -41,6 +41,7 @@ #include "runtime/vmThread.hpp" #include "utilities/defaultStream.hpp" #include "utilities/formatBuffer.hpp" +#include "utilities/globalDefinitions.hpp" bool CDSConfig::_is_dumping_static_archive = false; bool CDSConfig::_is_dumping_preimage_static_archive = false; @@ -123,6 +124,12 @@ void CDSConfig::ergo_initialize() { // etc), there is usually no need to attach to this JVM. FLAG_SET_ERGO(DisableAttachMechanism, true); } + + if (!AOTMetaspace::shared_base_valid((char*)SharedBaseAddress)) { + log_warning(cds)("SharedBaseAddress " PTR_FORMAT " is invalid. Reverting to " PTR_FORMAT, + p2i((void*)SharedBaseAddress), p2i((void*)DEFAULT_SHARED_BASE_ADDRESS)); + FLAG_SET_ERGO(SharedBaseAddress, DEFAULT_SHARED_BASE_ADDRESS); + } } const char* CDSConfig::default_archive_path() { diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 7df498ca5b9..640cde848b8 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -27,6 +27,9 @@ #include "runtime/globals_shared.hpp" +#define DEFAULT_SHARED_BASE_ADDRESS (LP64_ONLY(32*G) \ + NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0))) + // // Defines all globals flags used by CDS. // @@ -51,8 +54,7 @@ product(bool, PrintSharedArchiveAndExit, false, \ "Print shared archive file contents") \ \ - product(size_t, SharedBaseAddress, LP64_ONLY(32*G) \ - NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ + product(size_t, SharedBaseAddress, DEFAULT_SHARED_BASE_ADDRESS, \ "Address to allocate shared memory region for class data") \ range(0, SIZE_MAX) \ \ diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp index 712f7af4139..fd4ac33eb69 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp @@ -34,6 +34,7 @@ #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/copy.hpp" +#include "utilities/integerCast.hpp" #ifndef PRODUCT #define TRACE_BCEA(level, code) \ @@ -1077,17 +1078,32 @@ void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest, } } +bool BCEscapeAnalyzer::datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize) { + uint64_t datacount64 = (uint64_t)(numblocks + 1) * (stkSize + numLocals); + if (datacount64 > SIZE_MAX / sizeof(ArgumentMap)) { + return true; + } + datasize = integer_cast_permit_tautology(datacount64 * sizeof(ArgumentMap)); + return false; +} + void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { - int numblocks = _methodBlocks->num_blocks(); - int stkSize = _method->max_stack(); - int numLocals = _method->max_locals(); + uint numblocks = _methodBlocks->num_blocks(); + uint stkSize = _method->max_stack(); + uint numLocals = _method->max_locals(); StateInfo state; - int datacount = (numblocks + 1) * (stkSize + numLocals); - int datasize = datacount * sizeof(ArgumentMap); + size_t datasize; + if (datasize_overflow(numblocks, stkSize, numLocals, datasize)) { + _conservative = true; + return; + } + size_t datacount = datasize / sizeof(ArgumentMap); StateInfo *blockstates = (StateInfo *) arena->Amalloc(numblocks * sizeof(StateInfo)); ArgumentMap *statedata = (ArgumentMap *) arena->Amalloc(datasize); - for (int i = 0; i < datacount; i++) ::new ((void*)&statedata[i]) ArgumentMap(); + for (size_t i = 0; i < datacount; i++) { + ::new ((void*)&statedata[i]) ArgumentMap(); + } ArgumentMap *dp = statedata; state._vars = dp; dp += numLocals; @@ -1095,7 +1111,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { dp += stkSize; state._initialized = false; state._max_stack = stkSize; - for (int i = 0; i < numblocks; i++) { + for (uint i = 0; i < numblocks; i++) { blockstates[i]._vars = dp; dp += numLocals; blockstates[i]._stack = dp; @@ -1142,7 +1158,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { if (blk->is_handler() || blk->is_ret_target()) { // for an exception handler or a target of a ret instruction, we assume the worst case, // that any variable could contain any argument - for (int i = 0; i < numLocals; i++) { + for (uint i = 0; i < numLocals; i++) { state._vars[i] = allVars; } if (blk->is_handler()) { @@ -1155,7 +1171,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { state._stack[i] = allVars; } } else { - for (int i = 0; i < numLocals; i++) { + for (uint i = 0; i < numLocals; i++) { state._vars[i] = blkState->_vars[i]; } for (int i = 0; i < blkState->_stack_height; i++) { @@ -1170,7 +1186,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { DEBUG_ONLY(int handler_count = 0;) int blk_start = blk->start_bci(); int blk_end = blk->limit_bci(); - for (int i = 0; i < numblocks; i++) { + for (uint i = 0; i < numblocks; i++) { ciBlock *b = _methodBlocks->block(i); if (b->is_handler()) { int ex_start = b->ex_start_bci(); diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp index b75cb6a56f4..7bdd4a58146 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp @@ -152,6 +152,12 @@ class BCEscapeAnalyzer : public ArenaObj { // Copy dependencies from this analysis into "deps" void copy_dependencies(Dependencies *deps); + // Returns true if the datasize computation for iterate_blocks would + // overflow, i.e. the allocation size exceeds what can be represented. + // On success, sets datasize to the computed allocation size in bytes. + // Extracted as a public static method for testability (JDK-8216486). + static bool datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize); + #ifndef PRODUCT // dump escape information void dump(); diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index eecd9427585..c3805b4a054 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -76,8 +76,8 @@ class ciMethod : public ciMetadata { // Code attributes. int _code_size; - int _max_stack; - int _max_locals; + uint _max_stack; + u2 _max_locals; vmIntrinsicID _intrinsic_id; int _handler_count; int _interpreter_invocation_count; diff --git a/src/hotspot/share/ci/ciMethodBlocks.hpp b/src/hotspot/share/ci/ciMethodBlocks.hpp index f1b446c2a87..567ea1e39b4 100644 --- a/src/hotspot/share/ci/ciMethodBlocks.hpp +++ b/src/hotspot/share/ci/ciMethodBlocks.hpp @@ -38,7 +38,7 @@ private: Arena *_arena; GrowableArray *_blocks; ciBlock **_bci_to_block; - int _num_blocks; + u2 _num_blocks; int _code_size; void do_analysis(); diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index d1ea9c09d4c..95e1ef3b877 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -289,19 +289,6 @@ void ClassLoaderData::verify_not_claimed(int claim) { } #endif -bool ClassLoaderData::try_claim(int claim) { - for (;;) { - int old_claim = AtomicAccess::load(&_claim); - if ((old_claim & claim) == claim) { - return false; - } - int new_claim = old_claim | claim; - if (AtomicAccess::cmpxchg(&_claim, old_claim, new_claim) == old_claim) { - return true; - } - } -} - void ClassLoaderData::demote_strong_roots() { // The oop handle area contains strong roots that the GC traces from. We are about // to demote them to strong native oops that the GC does *not* trace from. Conceptually, @@ -369,11 +356,7 @@ void ClassLoaderData::dec_keep_alive_ref_count() { } } -void ClassLoaderData::oops_do(OopClosure* f, int claim_value, bool clear_mod_oops) { - if (claim_value != ClassLoaderData::_claim_none && !try_claim(claim_value)) { - return; - } - +void ClassLoaderData::oops_do_slow(OopClosure* f, bool clear_mod_oops) { // Only clear modified_oops after the ClassLoaderData is claimed. if (clear_mod_oops) { clear_modified_oops(); diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 64fcfb7519f..3a0a05126af 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -242,7 +242,7 @@ private: void verify_not_claimed(int claim) NOT_DEBUG_RETURN; bool claimed() const { return _claim != 0; } bool claimed(int claim) const { return (_claim & claim) == claim; } - bool try_claim(int claim); + inline bool try_claim(int claim); // Computes if the CLD is alive or not. This is safe to call in concurrent // contexts. @@ -305,7 +305,8 @@ private: void initialize_holder(Handle holder); - void oops_do(OopClosure* f, int claim_value, bool clear_modified_oops = false); + inline void oops_do(OopClosure* f, int claim_value, bool clear_modified_oops = false); + void oops_do_slow(OopClosure* f, bool clear_modified_oops); void classes_do(KlassClosure* klass_closure); Klass* klasses() { return _klasses; } diff --git a/src/hotspot/share/classfile/classLoaderData.inline.hpp b/src/hotspot/share/classfile/classLoaderData.inline.hpp index 4c4427b19e1..df29dca053b 100644 --- a/src/hotspot/share/classfile/classLoaderData.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderData.inline.hpp @@ -85,4 +85,23 @@ inline ClassLoaderData* ClassLoaderData::class_loader_data(oop loader) { return loader_data; } +inline bool ClassLoaderData::try_claim(int claim) { + for (;;) { + int old_claim = AtomicAccess::load(&_claim); + if ((old_claim & claim) == claim) { + return false; + } + int new_claim = old_claim | claim; + if (AtomicAccess::cmpxchg(&_claim, old_claim, new_claim) == old_claim) { + return true; + } + } +} + +inline void ClassLoaderData::oops_do(OopClosure* f, int claim_value, bool clear_mod_oops) { + if (claim_value == _claim_none || try_claim(claim_value)) { + oops_do_slow(f, clear_mod_oops); + } +} + #endif // SHARE_CLASSFILE_CLASSLOADERDATA_INLINE_HPP diff --git a/src/hotspot/share/classfile/javaAssertions.cpp b/src/hotspot/share/classfile/javaAssertions.cpp index 2a8f94d81b9..d2781e6e5e1 100644 --- a/src/hotspot/share/classfile/javaAssertions.cpp +++ b/src/hotspot/share/classfile/javaAssertions.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 @@ -83,13 +83,6 @@ void JavaAssertions::addOption(const char* name, bool enable) { if (name_copy[i] == JVM_SIGNATURE_DOT) name_copy[i] = JVM_SIGNATURE_SLASH; } - if (TraceJavaAssertions) { - tty->print_cr("JavaAssertions: adding %s %s=%d", - head == &_classes ? "class" : "package", - name_copy[0] != '\0' ? name_copy : "'default'", - enable); - } - // Prepend a new item to the list. Items added later take precedence, so // prepending allows us to stop searching the list after the first match. *head = new OptionList(name_copy, enable, *head); @@ -183,14 +176,6 @@ JavaAssertions::match_package(const char* classname) { return nullptr; } -inline void JavaAssertions::trace(const char* name, -const char* typefound, const char* namefound, bool enabled) { - if (TraceJavaAssertions) { - tty->print_cr("JavaAssertions: search for %s found %s %s=%d", - name, typefound, namefound[0] != '\0' ? namefound : "'default'", enabled); - } -} - bool JavaAssertions::enabled(const char* classname, bool systemClass) { assert(classname != nullptr, "must have a classname"); @@ -201,18 +186,15 @@ bool JavaAssertions::enabled(const char* classname, bool systemClass) { // First check options that apply to classes. If we find a match we're done. OptionList* p; if ((p = match_class(classname))) { - trace(classname, "class", p->name(), p->enabled()); return p->enabled(); } // Now check packages, from most specific to least. if ((p = match_package(classname))) { - trace(classname, "package", p->name(), p->enabled()); return p->enabled(); } // No match. Return the default status. bool result = systemClass ? systemClassDefault() : userClassDefault(); - trace(classname, systemClass ? "system" : "user", "default", result); return result; } diff --git a/src/hotspot/share/classfile/javaAssertions.hpp b/src/hotspot/share/classfile/javaAssertions.hpp index 58d03eacd48..3477e79c3c5 100644 --- a/src/hotspot/share/classfile/javaAssertions.hpp +++ b/src/hotspot/share/classfile/javaAssertions.hpp @@ -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 @@ -57,9 +57,6 @@ private: static void fillJavaArrays(const OptionList* p, int len, objArrayHandle names, typeArrayHandle status, TRAPS); - static inline void trace(const char* name, const char* typefound, - const char* namefound, bool enabled); - static inline OptionList* match_class(const char* classname); static OptionList* match_package(const char* classname); @@ -90,8 +87,6 @@ inline bool JavaAssertions::userClassDefault() { } inline void JavaAssertions::setUserClassDefault(bool enabled) { - if (TraceJavaAssertions) - tty->print_cr("JavaAssertions::setUserClassDefault(%d)", enabled); _userDefault = enabled; } @@ -100,8 +95,6 @@ inline bool JavaAssertions::systemClassDefault() { } inline void JavaAssertions::setSystemClassDefault(bool enabled) { - if (TraceJavaAssertions) - tty->print_cr("JavaAssertions::setSystemClassDefault(%d)", enabled); _sysDefault = enabled; } diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 48be24c20dc..8422a39827a 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -222,9 +222,9 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { split_verifier.verify_class(THREAD); exception_name = split_verifier.result(); - // If dumping {classic, final} static archive, don't bother to run the old verifier, as + // If dumping classic static archive, don't bother to run the old verifier, as // the class will be excluded from the archive anyway. - bool can_failover = !(CDSConfig::is_dumping_classic_static_archive() || CDSConfig::is_dumping_final_static_archive()) && + bool can_failover = !(CDSConfig::is_dumping_classic_static_archive()) && klass->major_version() < NOFAILOVER_MAJOR_VERSION; if (can_failover && !HAS_PENDING_EXCEPTION && // Split verifier doesn't set PENDING_EXCEPTION for failure @@ -233,9 +233,9 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name()); log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name()); #if INCLUDE_CDS - // Exclude any classes that are verified with the old verifier, as the old verifier - // doesn't call SystemDictionaryShared::add_verification_constraint() - if (CDSConfig::is_dumping_archive()) { + // Exclude any classes that are verified with the old verifier when the verification constraints + // cannot be preserved. + if (CDSConfig::is_dumping_archive() && !CDSConfig::is_preserving_verification_constraints()) { SystemDictionaryShared::log_exclusion(klass, "Verified with old verifier"); SystemDictionaryShared::set_excluded(klass); } @@ -244,6 +244,10 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { exception_message = message_buffer; exception_name = inference_verify( klass, message_buffer, message_buffer_len, THREAD); + + if (exception_name == nullptr && !HAS_PENDING_EXCEPTION) { + klass->set_fail_over_verified(); + } } if (exception_name != nullptr) { exception_message = split_verifier.exception_message(); diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 0348fae28b0..4337020846f 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -731,6 +731,7 @@ class SerializeClosure; template(runtimeSetup, "runtimeSetup") \ template(toFileURL_name, "toFileURL") \ template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ + template(sun_util_locale_BaseLocale, "sun/util/locale/BaseLocale") \ \ /* jcmd Thread.dump_to_file */ \ template(jdk_internal_vm_ThreadDumper, "jdk/internal/vm/ThreadDumper") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index b05ee22905d..b70f89b2645 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -83,11 +83,12 @@ const char* aot_code_entry_kind_name[] = { // 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()); + static LogStream inf_stream(LogLevel::Info, LogTagSetMapping::tagset()); if (RequireSharedSpaces || AbortVMOnAOTCodeFailure) { return err_stream; } else { - return dbg_stream; + static LogStream aot_stream(LogLevel::Info, LogTagSetMapping::tagset()); + return inf_stream.is_enabled() ? inf_stream : aot_stream; } } @@ -288,6 +289,20 @@ void AOTCodeCache::init2() { return; } + // Report contents of AOT code cache after verification passed + Header* header = opened_cache->_load_header; + if (header != nullptr) { // Loading AOT code + log_info (aot, codecache, init)("Loaded %u AOT code entries from AOT Code Cache", header->entries_count()); + log_debug(aot, codecache, init)(" Adapters: total=%u", header->adapters_count()); + log_debug(aot, codecache, init)(" Shared Blobs: total=%u", header->shared_blobs_count()); + log_debug(aot, codecache, init)(" StubGen Blobs: total=%d", header->stubgen_blobs_count()); + log_debug(aot, codecache, init)(" C1 Blobs: total=%u", header->C1_blobs_count()); + log_debug(aot, codecache, init)(" C2 Blobs: total=%u", header->C2_blobs_count()); + log_debug(aot, codecache, init)(" AOT code cache size: %u bytes", header->cache_size()); + + // Read strings + opened_cache->load_strings(); + } // initialize aot runtime constants as appropriate to this runtime AOTRuntimeConstants::initialize_from_runtime(); @@ -379,16 +394,6 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : set_failed(); return; } - 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()); - - // Read strings - load_strings(); } if (_for_dump) { _C_store_buffer = NEW_C_HEAP_ARRAY(char, max_aot_code_size() + DATA_ALIGNMENT, mtCode); @@ -2419,9 +2424,6 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB 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); - } const char* sub_name = (desc != nullptr) ? desc->name() : ""; assert(false, "Address " INTPTR_FORMAT " for Stub:%s is missing in AOT Code Cache addresses table", p2i(addr), sub_name); } else { diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 777ada59a0b..c65b9cb23d1 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -291,6 +291,7 @@ public: do_var(bool, UseCRC32Intrinsics) \ do_var(bool, UseDilithiumIntrinsics) \ do_var(bool, UseGHASHIntrinsics) \ + do_var(bool, UseIntPoly25519Intrinsics) \ do_var(bool, UseKyberIntrinsics) \ do_var(bool, UseMD5Intrinsics) \ do_var(bool, UsePoly1305Intrinsics) \ @@ -299,6 +300,7 @@ public: do_var(bool, UseSHA256Intrinsics) \ do_var(bool, UseSHA3Intrinsics) \ do_var(bool, UseSHA512Intrinsics) \ + do_var(bool, UseIntPolyIntrinsics) \ do_var(bool, UseVectorizedMismatchIntrinsic) \ do_fun(int, CompressedKlassPointers_shift, CompressedKlassPointers::shift()) \ do_fun(bool, JavaAssertions_systemClassDefault, JavaAssertions::systemClassDefault()) \ @@ -342,7 +344,6 @@ public: do_var(int, AVX3Threshold) /* array copy stubs and nmethods */ \ do_var(bool, EnableX86ECoreOpts) /* nmethods */ \ do_var(bool, UseLibmIntrinsic) \ - do_var(bool, UseIntPolyIntrinsics) \ // END #else #define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 6f3a1b09c48..efe5d6549eb 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -30,6 +30,7 @@ #include "code/dependencyContext.hpp" #include "code/nmethod.hpp" #include "code/pcDesc.hpp" +#include "code/vtableStubs.hpp" #include "compiler/compilationPolicy.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compilerDefinitions.inline.hpp" @@ -37,6 +38,7 @@ #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/classUnloadingContext.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shared/gcCause.hpp" #include "jfr/jfrEvents.hpp" #include "jvm_io.h" #include "logging/log.hpp" @@ -813,7 +815,8 @@ void CodeCache::update_cold_gc_count() { size_t used = max - free; double gc_interval = time - last_time; - _unloading_threshold_gc_requested = false; + AtomicAccess::store(&_unloading_threshold_gc_state, UnloadingRequestState::Idle); + _last_unloading_time = time; _last_unloading_used = used; @@ -888,7 +891,7 @@ void CodeCache::gc_on_allocation() { double free_ratio = double(free) / double(max); if (free_ratio <= StartAggressiveSweepingAt / 100.0) { // In case the GC is concurrent, we make sure only one thread requests the GC. - if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_requested, false, true) == false) { + if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_state, UnloadingRequestState::Idle, UnloadingRequestState::Active) == UnloadingRequestState::Idle) { log_info(codecache)("Triggering aggressive GC due to having only %.3f%% free memory", free_ratio * 100.0); Universe::heap()->collect(GCCause::_codecache_GC_aggressive); } @@ -914,7 +917,7 @@ void CodeCache::gc_on_allocation() { // it is eventually invoked to avoid trouble. if (allocated_since_last_ratio > threshold) { // In case the GC is concurrent, we make sure only one thread requests the GC. - if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_requested, false, true) == false) { + if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_state, UnloadingRequestState::Idle, UnloadingRequestState::Active) == UnloadingRequestState::Idle) { log_info(codecache)("Triggering threshold (%.3f%%) GC due to allocating %.3f%% since last unloading (%.3f%% used -> %.3f%% used)", threshold * 100.0, allocated_since_last_ratio * 100.0, last_used_ratio * 100.0, used_ratio * 100.0); Universe::heap()->collect(GCCause::_codecache_GC_threshold); @@ -934,7 +937,7 @@ uint64_t CodeCache::_cold_gc_count = INT_MAX; double CodeCache::_last_unloading_time = 0.0; size_t CodeCache::_last_unloading_used = 0; -volatile bool CodeCache::_unloading_threshold_gc_requested = false; +volatile CodeCache::UnloadingRequestState CodeCache::_unloading_threshold_gc_state = UnloadingRequestState::Idle; TruncatedSeq CodeCache::_unloading_gc_intervals(10 /* samples */); TruncatedSeq CodeCache::_unloading_allocation_rates(10 /* samples */); @@ -969,6 +972,23 @@ void CodeCache::on_gc_marking_cycle_finish() { update_cold_gc_count(); } +void CodeCache::defer_unloading_gc_request() { + assert_at_safepoint(); + assert(_unloading_threshold_gc_state == UnloadingRequestState::Active, "only defer active requests"); + AtomicAccess::store(&_unloading_threshold_gc_state, UnloadingRequestState::Deferred); +} + +void CodeCache::clear_deferred_unloading_gc_request() { + // Codecache marking may still be active after aborting gc marking, so we can not + // use is_marking_active() to check whether we are in the correct state to clear + // the deferred state. + // Requests are only deferred outside GC marking, and only cleared after + // at the end of whitebox, we can just clear it if it was Deferred. + AtomicAccess::cmpxchg(&_unloading_threshold_gc_state, + UnloadingRequestState::Deferred, + UnloadingRequestState::Idle); +} + void CodeCache::arm_all_nmethods() { BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); } @@ -1162,8 +1182,8 @@ size_t CodeCache::max_distance_to_non_nmethod() { CodeHeap* blob = get_code_heap(CodeBlobType::NonNMethod); // the max distance is minimized by placing the NonNMethod segment // in between MethodProfiled and MethodNonProfiled segments - size_t dist1 = (size_t)blob->high() - (size_t)_low_bound; - size_t dist2 = (size_t)_high_bound - (size_t)blob->low(); + size_t dist1 = (size_t)blob->high_boundary() - (size_t)_low_bound; + size_t dist2 = (size_t)_high_bound - (size_t)blob->low_boundary(); return dist1 > dist2 ? dist1 : dist2; } } @@ -1971,8 +1991,8 @@ void CodeCache::write_perf_map(const char* filename, outputStream* st) { AllCodeBlobsIterator iter(AllCodeBlobsIterator::not_unloading); while (iter.next()) { CodeBlob *cb = iter.method(); - if (is_stub_code_blob(cb)) { - // Individual stub routines are dumped after the main loop. + if (is_stub_code_blob(cb) || cb->is_vtable_blob()) { + // Individual stub routines and vtable stubs are dumped after the main loop. continue; } ResourceMark rm; @@ -1991,6 +2011,13 @@ void CodeCache::write_perf_map(const char* filename, outputStream* st) { (intptr_t)d->begin(), (intptr_t)d->size_in_bytes(), d->group(), d->name()); } + VtableStubs::vtable_stub_do([&](VtableStub* s) { + fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s [%d]", + (intptr_t)s->code_begin(), + (intptr_t)s->code_size(), + s->is_vtable_stub() ? "vtable stub" : "itable stub", + s->index()); + }); } #endif // LINUX diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 3b8aa5b2e58..bef114e5e19 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -107,7 +107,12 @@ class CodeCache : AllStatic { static double _last_unloading_time; static TruncatedSeq _unloading_gc_intervals; static TruncatedSeq _unloading_allocation_rates; - static volatile bool _unloading_threshold_gc_requested; + enum UnloadingRequestState : uint { + Idle, + Active, + Deferred + }; + static volatile UnloadingRequestState _unloading_threshold_gc_state; static ExceptionCache* volatile _exception_cache_purge_list; @@ -198,6 +203,9 @@ class CodeCache : AllStatic { static uint64_t previous_completed_gc_marking_cycle(); static void on_gc_marking_cycle_start(); static void on_gc_marking_cycle_finish(); + + static void defer_unloading_gc_request(); + static void clear_deferred_unloading_gc_request(); // Arm nmethods so that special actions are taken (nmethod_entry_barrier) for // on-stack nmethods. It's used in two places: // 1. Used before the start of concurrent marking so that oops inside diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 27f01797d39..5d7df498102 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -3783,9 +3783,6 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { address dest = r->destination(); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); - } if (desc != nullptr) { st.print(" Stub::%s", desc->name()); return st.as_string(); diff --git a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp index c37651892cc..93c74be27b9 100644 --- a/src/hotspot/share/code/oopRecorder.cpp +++ b/src/hotspot/share/code/oopRecorder.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 @@ -302,9 +302,6 @@ void ExternalsRecorder::print_statistics() { if (addr != nullptr) { if (StubRoutines::contains(addr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); - } const char* stub_name = (desc != nullptr) ? desc->name() : ""; tty->print(" stub: %s", stub_name); } else { diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 73e4b6de7b4..5295dc0f287 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.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 @@ -922,9 +922,6 @@ void RelocIterator::print_current_on(outputStream* st) { st->print(" | [destination=" INTPTR_FORMAT "]", p2i(dest)); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); - } if (desc != nullptr) { st->print(" Stub::%s", desc->name()); } diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp index 35b226a8798..df3e80bea41 100644 --- a/src/hotspot/share/code/vtableStubs.cpp +++ b/src/hotspot/share/code/vtableStubs.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 @@ -319,15 +319,6 @@ void vtableStubs_init() { VtableStubs::initialize(); } -void VtableStubs::vtable_stub_do(void f(VtableStub*)) { - for (int i = 0; i < N; i++) { - for (VtableStub* s = AtomicAccess::load_acquire(&_table[i]); s != nullptr; s = s->next()) { - f(s); - } - } -} - - //----------------------------------------------------------------------------------------------------- // Non-product code #ifndef PRODUCT diff --git a/src/hotspot/share/code/vtableStubs.hpp b/src/hotspot/share/code/vtableStubs.hpp index 06acd8f25b9..fd06bf7c647 100644 --- a/src/hotspot/share/code/vtableStubs.hpp +++ b/src/hotspot/share/code/vtableStubs.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 @@ -28,6 +28,7 @@ #include "asm/macroAssembler.hpp" #include "code/vmreg.hpp" #include "memory/allStatic.hpp" +#include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" // A VtableStub holds an individual code stub for a pair (vtable index, #args) for either itables or vtables @@ -111,7 +112,9 @@ class VtableStubs : AllStatic { static bool contains(address pc); // is pc within any stub? static VtableStub* stub_containing(address pc); // stub containing pc or nullptr static void initialize(); - static void vtable_stub_do(void f(VtableStub*)); // iterates over all vtable stubs + + template + static void vtable_stub_do(F f); }; @@ -142,13 +145,14 @@ class VtableStub { : _next(nullptr), _index(index), _ame_offset(-1), _npe_offset(-1), _type(is_vtable_stub ? Type::vtable_stub : Type::itable_stub) {} VtableStub* next() const { return _next; } - int index() const { return _index; } static VMReg receiver_location() { return _receiver_location; } void set_next(VtableStub* n) { _next = n; } public: + int index() const { return _index; } + int code_size() const { return VtableStubs::code_size_limit(is_vtable_stub()); } address code_begin() const { return (address)(this + 1); } - address code_end() const { return code_begin() + VtableStubs::code_size_limit(is_vtable_stub()); } + address code_end() const { return code_begin() + code_size(); } address entry_point() const { return code_begin(); } static int entry_offset() { return sizeof(class VtableStub); } @@ -189,4 +193,13 @@ class VtableStub { }; +template +void VtableStubs::vtable_stub_do(F f) { + for (int i = 0; i < N; i++) { + for (VtableStub* s = AtomicAccess::load_acquire(&_table[i]); s != nullptr; s = s->next()) { + f(s); + } + } +} + #endif // SHARE_CODE_VTABLESTUBS_HPP diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 94e734aaad5..81c03587416 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -1377,7 +1377,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& // Determine if a method should be compiled with a normal entry point at a different level. CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD) { CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(), common(method, cur_level, THREAD, true)); - CompLevel next_level = common(method, cur_level, THREAD, !TrainingData::have_data() && is_old(method)); + CompLevel next_level = common(method, cur_level, THREAD, is_old(method)); // If OSR method level is greater than the regular method level, the levels should be // equalized by raising the regular method level in order to avoid OSRs during each diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 69c9bc585f7..1ad667e51f1 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -272,7 +272,11 @@ void CompilerConfig::set_compilation_policy_flags() { } #ifdef COMPILER2 - if (HotCodeHeap) { + if (HotCodeHeap && !is_c2_enabled()) { + warning("HotCodeHeap disabled because C2 is disabled."); + FLAG_SET_ERGO(HotCodeHeap, false); + FLAG_SET_ERGO(HotCodeHeapSize, 0); + } else if (HotCodeHeap) { if (FLAG_IS_DEFAULT(SegmentedCodeCache)) { FLAG_SET_ERGO(SegmentedCodeCache, true); } else if (!SegmentedCodeCache) { @@ -285,10 +289,6 @@ void CompilerConfig::set_compilation_policy_flags() { 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"); } @@ -472,6 +472,10 @@ void CompilerConfig::ergo_initialize() { } #ifdef COMPILER2 + // Xcomp has no reasonable profiling information, enable inlining cold methods + if (Arguments::is_compiler_only() && FLAG_IS_DEFAULT(InlineColdMethods)) { + FLAG_SET_DEFAULT(InlineColdMethods, true); + } if (!EliminateLocks) { EliminateNestedLocks = false; } diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp index e1f8d9f8922..effe6cc0725 100644 --- a/src/hotspot/share/compiler/compiler_globals.hpp +++ b/src/hotspot/share/compiler/compiler_globals.hpp @@ -273,7 +273,7 @@ "mode if posssible") \ \ product(ccstr, CompilationMode, "default", \ - "Compilation modes: " \ + "(Deprecated) Compilation modes: " \ "default: normal tiered compilation; " \ "quick-only: C1-only mode; " \ "high-only: C2-only mode.") \ diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index 2c1ef235e07..9dc8956d98d 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.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 @@ -591,9 +591,6 @@ void decode_env::print_address(address adr) { if (Universe::is_fully_initialized()) { if (StubRoutines::contains(adr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(adr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); - } if (desc != nullptr) { st->print("Stub::%s", desc->name()); if (desc->begin() != adr) { diff --git a/src/hotspot/share/gc/g1/g1CardSet.hpp b/src/hotspot/share/gc/g1/g1CardSet.hpp index 64ddf0ca6a4..21a2f5f045e 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.hpp +++ b/src/hotspot/share/gc/g1/g1CardSet.hpp @@ -185,13 +185,11 @@ public: // it. // See its description below for more information. class G1CardSet : public CHeapObj { - friend class G1CardSetTest; friend class G1CardSetMtTestTask; + friend class G1CardSetTest; friend class G1CheckCardClosure; - - friend class G1TransferCard; - friend class G1ReleaseCardsets; + friend class G1TransferCard; // When splitting addresses into region and card within that region, the logical // shift value to get the region. diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp index ca4487876b9..7f1dec462d4 100644 --- a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp +++ b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp @@ -196,12 +196,12 @@ public: clean(delete_check); } - // Calculate the log2 of the table size we want to shrink to. - size_t log2_target_shrink_size(size_t current_size) const { + // Calculate the log2 of the table size we want to change to. + size_t log2_target_size(size_t new_size) const { // A table with the new size should be at most filled by this factor. Otherwise // we would grow again quickly. const float WantedLoadFactor = 0.5; - size_t min_expected_size = checked_cast(ceil(current_size / WantedLoadFactor)); + size_t min_expected_size = checked_cast(ceil(new_size / WantedLoadFactor)); size_t result = Log2DefaultNumBuckets; if (min_expected_size != 0) { @@ -214,12 +214,34 @@ public: // Shrink to keep table size appropriate to the given number of entries. void shrink_to_match(size_t current_size) { size_t prev_log2size = _table.get_size_log2(Thread::current()); - size_t new_log2_table_size = log2_target_shrink_size(current_size); + size_t new_log2_table_size = log2_target_size(current_size); if (new_log2_table_size < prev_log2size) { _table.shrink(Thread::current(), new_log2_table_size); } } + void grow_to_match_unsafe(size_t new_size) { + assert_at_safepoint(); + + size_t prev_log2size = _table.get_size_log2(Thread::current()); + size_t new_log2_table_size = log2_target_size(new_size); + // If there is nothing in the table, we can reset directly. Otherwise double + // the table in size until the target is reached, which is the only grow + // operation CHT supports. + if ((prev_log2size != new_log2_table_size) && (number_of_entries() == 0)) { + _table.unsafe_reset(new_log2_table_size); + } else { + while (new_log2_table_size > prev_log2size) { + if (!_table.grow(Thread::current(), new_log2_table_size)) { + // Should always succeed during safepoint. + ShouldNotReachHere(); + break; + } + prev_log2size = _table.get_size_log2(Thread::current()); + } + } + } + void reset_table_scanner() { _table_scanner.set(&_table, BucketClaimSize); } @@ -269,6 +291,11 @@ void G1CodeRootSet::bulk_remove() { _table->bulk_remove(); } +void G1CodeRootSet::prepare_for_adding_code_roots(size_t num_new_code_roots) { + assert(!_is_iterating, "should not mutate while iterating the table"); + _table->grow_to_match_unsafe(_table->number_of_entries() + num_new_code_roots); +} + bool G1CodeRootSet::contains(nmethod* method) { return _table->contains(method); } diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.hpp b/src/hotspot/share/gc/g1/g1CodeRootSet.hpp index ffa1cddbe54..b298bbfb914 100644 --- a/src/hotspot/share/gc/g1/g1CodeRootSet.hpp +++ b/src/hotspot/share/gc/g1/g1CodeRootSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -45,6 +45,10 @@ class G1CodeRootSet { void add(nmethod* method); bool remove(nmethod* method); void bulk_remove(); + // Notify the code root set that we are about to add the given + // number of code roots. Only to be used during safepoint, not + // in parallel to other modifications. + void prepare_for_adding_code_roots(size_t num_code_roots); bool contains(nmethod* method); void clear(); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 8ea880c820f..f60ce9b15b4 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -61,7 +61,7 @@ #include "gc/g1/g1RegionPinCache.inline.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "gc/g1/g1RemSet.hpp" -#include "gc/g1/g1ReviseYoungLengthTask.hpp" +#include "gc/g1/g1ReviseNumYoungRegionsTask.hpp" #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/g1/g1ServiceThread.hpp" @@ -846,7 +846,7 @@ void G1CollectedHeap::prepare_heap_for_full_collection() { _hrm.remove_all_free_regions(); } -void G1CollectedHeap::verify_before_full_collection() { +void G1CollectedHeap::verify_before_full_collection(bool concurrent_cycle_aborted) { assert_used_and_recalculate_used_equal(this); if (!VerifyBeforeGC) { return; @@ -856,7 +856,7 @@ void G1CollectedHeap::verify_before_full_collection() { } _verifier->verify_region_sets_optional(); _verifier->verify_before_gc(); - _verifier->verify_bitmap_clear(true /* above_tams_only */); + _verifier->verify_bitmap_clear(true /* above_tams_only */, concurrent_cycle_aborted); } void G1CollectedHeap::prepare_for_mutator_after_full_collection(size_t allocation_word_size) { @@ -868,7 +868,7 @@ void G1CollectedHeap::prepare_for_mutator_after_full_collection(size_t allocatio // Rebuild the code root lists for each region rebuild_code_roots(); - finish_codecache_marking_cycle(); + CodeCache::arm_all_nmethods(); start_new_collection_set(); _allocator->init_mutator_alloc_regions(); @@ -916,7 +916,7 @@ void G1CollectedHeap::verify_after_full_collection() { // At this point there should be no regions in the // entire heap tagged as young. - assert(check_young_list_empty(), "young list should be empty at this point"); + assert(check_no_young_regions(), "We should not have young regions at this point"); // Note: since we've just done a full GC, concurrent // marking is no longer active. Therefore we need not @@ -1295,7 +1295,7 @@ G1CollectedHeap::G1CollectedHeap() : _service_thread(nullptr), _periodic_gc_task(nullptr), _free_arena_memory_task(nullptr), - _revise_young_length_task(nullptr), + _revise_num_young_regions_task(nullptr), _workers(nullptr), _refinement_epoch(0), _last_synchronized_start(0), @@ -1604,9 +1604,9 @@ jint G1CollectedHeap::initialize() { _free_arena_memory_task = new G1MonotonicArenaFreeMemoryTask("Card Set Free Memory Task"); _service_thread->register_task(_free_arena_memory_task); - if (policy()->use_adaptive_young_list_length()) { - _revise_young_length_task = new G1ReviseYoungLengthTask("Revise Young Length List Task"); - _service_thread->register_task(_revise_young_length_task); + if (policy()->use_adaptive_num_young_regions()) { + _revise_num_young_regions_task = new G1ReviseNumYoungRegionsTask("Revise Num Young Regions Task"); + _service_thread->register_task(_revise_num_young_regions_task); } // Here we allocate the dummy G1HeapRegion that is required by the @@ -1654,6 +1654,9 @@ void G1CollectedHeap::stop() { // that are destroyed during shutdown. _cr->stop(); _service_thread->stop(); + VM_G1StopMarking op; + VMThread::execute(&op); + _cm->stop(); } @@ -1758,12 +1761,11 @@ size_t G1CollectedHeap::unused_committed_regions_in_bytes() const { // Computes the sum of the storage used by the various regions. size_t G1CollectedHeap::used() const { - size_t result = _summary_bytes_used + _allocator->used_in_alloc_regions(); - return result; + return used_unlocked() + _allocator->used_in_alloc_regions(); } size_t G1CollectedHeap::used_unlocked() const { - return _summary_bytes_used; + return _summary_bytes_used.load_relaxed(); } class SumUsedClosure: public G1HeapRegionClosure { @@ -1799,12 +1801,12 @@ bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) { } void G1CollectedHeap::increment_old_marking_cycles_started() { - assert(_old_marking_cycles_started == _old_marking_cycles_completed || - _old_marking_cycles_started == _old_marking_cycles_completed + 1, - "Wrong marking cycle count (started: %d, completed: %d)", - _old_marking_cycles_started, _old_marking_cycles_completed); + assert(old_marking_cycles_started() == old_marking_cycles_completed() || + old_marking_cycles_started() == old_marking_cycles_completed() + 1, + "Wrong marking cycle count (started: %u, completed: %u)", + old_marking_cycles_started(), old_marking_cycles_completed()); - _old_marking_cycles_started++; + _old_marking_cycles_started.add_then_fetch(1u, memory_order_relaxed); } void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent, @@ -1825,21 +1827,21 @@ void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent, // This is the case for the inner caller, i.e. a Full GC. assert(concurrent || - (_old_marking_cycles_started == _old_marking_cycles_completed + 1) || - (_old_marking_cycles_started == _old_marking_cycles_completed + 2), - "for inner caller (Full GC): _old_marking_cycles_started = %u " - "is inconsistent with _old_marking_cycles_completed = %u", - _old_marking_cycles_started, _old_marking_cycles_completed); + (old_marking_cycles_started() == old_marking_cycles_completed() + 1) || + (old_marking_cycles_started() == old_marking_cycles_completed() + 2), + "for inner caller (Full GC): old_marking_cycles_started = %u " + "is inconsistent with old_marking_cycles_completed = %u", + old_marking_cycles_started(), old_marking_cycles_completed()); // This is the case for the outer caller, i.e. the concurrent cycle. assert(!concurrent || - (_old_marking_cycles_started == _old_marking_cycles_completed + 1), + (old_marking_cycles_started() == old_marking_cycles_completed() + 1), "for outer caller (concurrent cycle): " - "_old_marking_cycles_started = %u " - "is inconsistent with _old_marking_cycles_completed = %u", - _old_marking_cycles_started, _old_marking_cycles_completed); + "old_marking_cycles_started = %u " + "is inconsistent with old_marking_cycles_completed = %u", + old_marking_cycles_started(), old_marking_cycles_completed()); - _old_marking_cycles_completed += 1; + _old_marking_cycles_completed.add_then_fetch(1u, memory_order_relaxed); if (whole_heap_examined) { // Signal that we have completed a visit to all live objects. record_whole_heap_examined_timestamp(); @@ -1904,7 +1906,7 @@ bool G1CollectedHeap::wait_full_mark_finished(GCCause::Cause cause, // while completed_now < started_after. LOG_COLLECT_CONCURRENTLY(cause, "wait"); MonitorLocker ml(G1OldGCCount_lock); - while (gc_counter_less_than(_old_marking_cycles_completed, + while (gc_counter_less_than(old_marking_cycles_completed(), old_marking_started_after)) { ml.wait(); } @@ -1929,7 +1931,7 @@ static bool should_retry_vm_op(GCCause::Cause cause, // GC, so try again. LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress"); return true; - } else if (op->whitebox_attached()) { + } else if (op->whitebox_controlled()) { // If WhiteBox wants control, wait for notification of a state // change in the controller, then try again. Don't wait for // release of control, since collections may complete while in @@ -1986,8 +1988,8 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, // more recent collection. That's what we want, rather than having // our retry possibly perform an unnecessary collection. gc_counter = total_collections(); - old_marking_started_after = _old_marking_cycles_started; - old_marking_completed_after = _old_marking_cycles_completed; + old_marking_started_after = old_marking_cycles_started(); + old_marking_completed_after = old_marking_cycles_completed(); } if (cause == GCCause::_wb_breakpoint) { @@ -1997,7 +1999,7 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, } // When _wb_breakpoint there can't be another cycle or deferred. assert(!op.cycle_already_in_progress(), "invariant"); - assert(!op.whitebox_attached(), "invariant"); + assert(!op.whitebox_controlled(), "invariant"); // Concurrent cycle attempt might have been cancelled by some other // collection, so retry. Unlike other cases below, we want to retry // even if cancelled by a STW full collection, because we really want @@ -2022,15 +2024,21 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, // Cases (2) and (3) are detected together by a change to // _old_marking_cycles_started. // - // Compared to other "automatic" GCs (see below), we do not consider being - // in whitebox as sufficient too because we might be anywhere within that - // cycle and we need to make progress. + // Compared to other "automatic" GCs (see below), being in WhiteBox is not + // addressed here because we need to handle it specially. if (op.mark_in_progress() || (old_marking_started_before != old_marking_started_after)) { LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true); return true; } + if (op.whitebox_controlled()) { + LOG_COLLECT_CONCURRENTLY(cause, "Suppressed CodeCache GC because of WhiteBox in control."); + // The caller in this case does not check the return value, so it does not + // really matter what we return. However we did not finish the request. + return false; + } + if (wait_full_mark_finished(cause, old_marking_started_before, old_marking_started_after, @@ -2038,7 +2046,11 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, return true; } - if (should_retry_vm_op(cause, &op)) { + if (op.cycle_already_in_progress()) { + // If VMOp failed because a cycle was already in progress, it + // is now complete (we just waited). But it didn't finish this + // request, so try again. + LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress"); continue; } } else if (!GCCause::is_user_requested_gc(cause)) { @@ -2059,7 +2071,7 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, // _old_marking_cycles_started. if (op.gc_succeeded() || op.cycle_already_in_progress() || - op.whitebox_attached() || + op.whitebox_controlled() || (old_marking_started_before != old_marking_started_after)) { LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true); return true; @@ -2282,7 +2294,7 @@ bool G1CollectedHeap::block_is_obj(const HeapWord* addr) const { } size_t G1CollectedHeap::tlab_capacity() const { - return eden_target_length() * G1HeapRegion::GrainBytes; + return target_num_eden_regions() * G1HeapRegion::GrainBytes; } size_t G1CollectedHeap::tlab_used() const { @@ -2451,7 +2463,7 @@ G1HeapSummary G1CollectedHeap::create_g1_heap_summary() { size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); size_t eden_capacity_bytes = - (policy()->young_list_target_length() * G1HeapRegion::GrainBytes) - survivor_used_bytes; + (policy()->target_num_young_regions() * G1HeapRegion::GrainBytes) - survivor_used_bytes; VirtualSpaceSummary heap_summary = create_heap_space_summary(); return G1HeapSummary(heap_summary, heap_used, eden_used_bytes, eden_capacity_bytes, @@ -2723,13 +2735,14 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si _bytes_used_during_gc = 0; - _cm->fully_initialize(); - policy()->decide_on_concurrent_start_pause(); // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already // been reset for the next pause. bool should_start_concurrent_mark_operation = collector_state()->is_in_concurrent_start_gc(); + if (should_start_concurrent_mark_operation) { + _cm->fully_initialize(); + } // Perform the collection. G1YoungCollector collector(gc_cause(), allocation_word_size); @@ -2880,6 +2893,7 @@ void G1CollectedHeap::free_region(G1HeapRegion* hr, G1FreeRegionList* free_list) // Reset region metadata to allow reuse. hr->hr_clear(true /* clear_space */); + concurrent_mark()->reset_region_marking_state(hr); _policy->remset_tracker()->update_at_free(hr); if (free_list != nullptr) { @@ -2988,7 +3002,7 @@ public: bool success() { return _success; } }; -bool G1CollectedHeap::check_young_list_empty() { +bool G1CollectedHeap::check_no_young_regions() { bool ret = (young_regions_count() == 0); NoYoungRegionsClosure closure; @@ -3007,8 +3021,8 @@ void G1CollectedHeap::prepare_region_for_full_compaction(G1HeapRegion* hr) { } else if (hr->is_old()) { _old_set.remove(hr); } else if (hr->is_young()) { - // Note that emptying the eden and survivor lists is postponed and instead - // done as the first step when rebuilding the regions sets again. The reason + // Note that clearing eden and survivor region tracking is postponed and + // done as the first step when rebuilding the region sets again. The reason // for this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. hr->uninstall_surv_rate_group(); @@ -3019,18 +3033,18 @@ void G1CollectedHeap::prepare_region_for_full_compaction(G1HeapRegion* hr) { } void G1CollectedHeap::increase_used(size_t bytes) { - _summary_bytes_used += bytes; + _summary_bytes_used.add_then_fetch(bytes, memory_order_relaxed); } void G1CollectedHeap::decrease_used(size_t bytes) { - assert(_summary_bytes_used >= bytes, + assert(used_unlocked() >= bytes, "invariant: _summary_bytes_used: %zu should be >= bytes: %zu", - _summary_bytes_used, bytes); - _summary_bytes_used -= bytes; + used_unlocked(), bytes); + _summary_bytes_used.sub_then_fetch(bytes, memory_order_relaxed); } void G1CollectedHeap::set_used(size_t bytes) { - _summary_bytes_used = bytes; + _summary_bytes_used.store_relaxed(bytes); } class RebuildRegionSetsClosure : public G1HeapRegionClosure { @@ -3192,6 +3206,9 @@ G1HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegio // Synchronize with region attribute table. update_region_attr(new_alloc_region); } + + _cm->notify_new_region(new_alloc_region); + G1HeapRegionPrinter::alloc(new_alloc_region); return new_alloc_region; } @@ -3209,8 +3226,8 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, _survivor.add_used_bytes(allocated_bytes); } - bool const during_im = collector_state()->is_in_concurrent_start_gc(); - if (during_im && allocated_bytes > 0) { + bool in_concurrent_start_gc = collector_state()->is_in_concurrent_start_gc(); + if (in_concurrent_start_gc && allocated_bytes > 0) { _cm->add_root_region(alloc_region); } G1HeapRegionPrinter::retire(alloc_region); @@ -3325,8 +3342,3 @@ void G1CollectedHeap::start_codecache_marking_cycle_if_inactive(bool concurrent_ CodeCache::arm_all_nmethods(); } } - -void G1CollectedHeap::finish_codecache_marking_cycle() { - CodeCache::on_gc_marking_cycle_finish(); - CodeCache::arm_all_nmethods(); -} diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index a68d1030636..672dea9b7b0 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -75,7 +75,7 @@ class G1GCPhaseTimes; class G1HeapSizingPolicy; class G1NewTracer; class G1RemSet; -class G1ReviseYoungLengthTask; +class G1ReviseNumYoungRegionsTask; class G1ServiceTask; class G1ServiceThread; class GCMemoryManager; @@ -146,27 +146,20 @@ public: }; class G1CollectedHeap : public CollectedHeap { + friend class G1CheckRegionAttrTableClosure; + friend class G1EvacuateRegionsTask; + friend class G1FullCollector; + friend class G1GCAllocRegion; + friend class G1HeapPrinterMark; + friend class G1HeapRegionClaimer; + friend class G1HeapVerifier; + friend class G1PLABAllocator; + friend class G1YoungGCVerifierMark; + friend class MutatorAllocRegion; friend class VM_G1CollectForAllocation; friend class VM_G1CollectFull; friend class VM_G1TryInitiateConcMark; friend class VMStructs; - friend class MutatorAllocRegion; - friend class G1FullCollector; - friend class G1GCAllocRegion; - friend class G1HeapVerifier; - - friend class G1YoungGCVerifierMark; - - // Closures used in implementation. - friend class G1EvacuateRegionsTask; - friend class G1PLABAllocator; - - // Other related classes. - friend class G1HeapPrinterMark; - friend class G1HeapRegionClaimer; - - // Testing classes. - friend class G1CheckRegionAttrTableClosure; private: // GC Overhead Limit functionality related members. @@ -183,7 +176,7 @@ private: G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task; - G1ReviseYoungLengthTask* _revise_young_length_task; + G1ReviseNumYoungRegionsTask* _revise_num_young_regions_task; WorkerThreads* _workers; @@ -250,7 +243,7 @@ private: // Outside of GC pauses, the number of bytes used in all regions other // than the current allocation region(s). - volatile size_t _summary_bytes_used; + Atomic _summary_bytes_used; void increase_used(size_t bytes); void decrease_used(size_t bytes); @@ -320,11 +313,11 @@ private: // Keeps track of how many "old marking cycles" (i.e., Full GCs or // concurrent cycles) we have started. - volatile uint _old_marking_cycles_started; + Atomic _old_marking_cycles_started; // Keeps track of how many "old marking cycles" (i.e., Full GCs or // concurrent cycles) we have completed. - volatile uint _old_marking_cycles_completed; + Atomic _old_marking_cycles_completed; // Create a memory mapper for auxiliary data structures of the given size and // translation factor. @@ -401,7 +394,6 @@ private: #define assert_used_and_recalculate_used_equal(g1h) do {} while(0) #endif - // The young region list. G1EdenRegions _eden; G1SurvivorRegions _survivor; @@ -531,7 +523,7 @@ private: // Internal helpers used during full GC to split it up to // increase readability. bool abort_concurrent_cycle(); - void verify_before_full_collection(); + void verify_before_full_collection(bool concurrent_cycle_aborted); void prepare_heap_for_full_collection(); void prepare_for_mutator_after_full_collection(size_t allocation_word_size); void abort_refinement(); @@ -698,11 +690,11 @@ public: void increment_old_marking_cycles_completed(bool concurrent, bool whole_heap_examined); uint old_marking_cycles_started() const { - return _old_marking_cycles_started; + return _old_marking_cycles_started.load_relaxed(); } uint old_marking_cycles_completed() const { - return _old_marking_cycles_completed; + return _old_marking_cycles_completed.load_relaxed(); } // Allocates a new heap region instance. @@ -961,7 +953,6 @@ public: void fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap) override; static void start_codecache_marking_cycle_if_inactive(bool concurrent_mark_start); - static void finish_codecache_marking_cycle(); // The shared block offset table array. G1BlockOffsetTable* bot() const { return _bot; } @@ -1244,7 +1235,7 @@ public: G1SurvivorRegions* survivor() { return &_survivor; } - inline uint eden_target_length() const; + inline uint target_num_eden_regions() const; uint eden_regions_count() const { return _eden.length(); } uint eden_regions_count(uint node_index) const { return _eden.regions_on_node(node_index); } uint survivor_regions_count() const { return _survivor.length(); } @@ -1256,7 +1247,7 @@ public: uint humongous_regions_count() const { return _humongous_set.length(); } #ifdef ASSERT - bool check_young_list_empty(); + bool check_no_young_regions(); #endif bool is_marked(oop obj) const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index bad9ac18eec..5d23a7d463e 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -295,8 +295,8 @@ inline bool G1CollectedHeap::is_collection_set_candidate(const G1HeapRegion* r) return candidates->contains(r); } -inline uint G1CollectedHeap::eden_target_length() const { - return _policy->young_list_target_length() - survivor_regions_count(); +inline uint G1CollectedHeap::target_num_eden_regions() const { + return _policy->target_num_young_regions() - survivor_regions_count(); } #endif // SHARE_GC_G1_G1COLLECTEDHEAP_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 7329e679519..3a086d8b09b 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -33,17 +33,16 @@ #include "gc/g1/g1ParScanThreadState.hpp" #include "gc/g1/g1Policy.hpp" #include "logging/logStream.hpp" -#include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -uint G1CollectionSet::groups_cur_length() const { +uint G1CollectionSet::num_groups() const { assert(_inc_build_state == CSetBuildType::Inactive, "must be"); return _groups.length(); } -uint G1CollectionSet::groups_increment_length() const { - return groups_cur_length() - _groups_inc_part_start; +uint G1CollectionSet::num_groups_in_increment() const { + return num_groups() - _groups_inc_part_start; } G1CollectorState* G1CollectionSet::collector_state() const { @@ -59,12 +58,12 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) : _policy(policy), _candidates(), _regions(nullptr), - _regions_max_length(0), - _regions_cur_length(0), + _max_num_regions(0), + _num_regions(0), _groups(), - _eden_region_length(0), - _survivor_region_length(0), - _initial_old_region_length(0), + _num_eden_regions(0), + _num_survivor_regions(0), + _num_initial_old_regions(0), _optional_groups(), DEBUG_ONLY(_inc_build_state(CSetBuildType::Inactive) COMMA) _regions_inc_part_start(0), @@ -76,27 +75,27 @@ G1CollectionSet::~G1CollectionSet() { abandon_all_candidates(); } -void G1CollectionSet::init_region_lengths(uint eden_cset_region_length, - uint survivor_cset_region_length) { +void G1CollectionSet::prepare_for_collection(uint num_eden_cset_regions, + uint num_survivor_cset_regions) { assert_at_safepoint_on_vm_thread(); - _eden_region_length = eden_cset_region_length; - _survivor_region_length = survivor_cset_region_length; + _num_eden_regions = num_eden_cset_regions; + _num_survivor_regions = num_survivor_cset_regions; - assert((size_t)young_region_length() == _regions_cur_length, - "Young region length %u should match collection set length %u", young_region_length(), _regions_cur_length); + assert(num_young_regions() == num_regions(), + "Young region amount %u should match collection set region amount %u", num_young_regions(), num_regions()); - _initial_old_region_length = 0; + _num_initial_old_regions = 0; assert(_optional_groups.length() == 0, "Should not have any optional groups yet"); _optional_groups.clear(); } -void G1CollectionSet::initialize(uint max_region_length) { +void G1CollectionSet::initialize(uint max_num_regions) { guarantee(_regions == nullptr, "Must only initialize once."); - _regions_max_length = max_region_length; - _regions = NEW_C_HEAP_ARRAY(uint, max_region_length, mtGC); + _max_num_regions = max_num_regions; + _regions = NEW_C_HEAP_ARRAY(uint, max_num_regions, mtGC); - _candidates.initialize(max_region_length); + _candidates.initialize(max_num_regions); } void G1CollectionSet::abandon() { @@ -109,7 +108,7 @@ void G1CollectionSet::abandon() { void G1CollectionSet::abandon_all_candidates() { _candidates.clear(); - _initial_old_region_length = 0; + _num_initial_old_regions = 0; } void G1CollectionSet::prepare_for_scan () { @@ -128,17 +127,21 @@ void G1CollectionSet::add_old_region(G1HeapRegion* hr) { _g1h->register_old_collection_set_region_with_region_attr(hr); - assert(_regions_cur_length < _regions_max_length, "Collection set now larger than maximum size."); - _regions[_regions_cur_length++] = hr->hrm_index(); - _initial_old_region_length++; + uint local_num_regions = num_regions(); + assert(local_num_regions < _max_num_regions, "Collection set now larger than maximum size."); + _regions[local_num_regions] = hr->hrm_index(); + _num_regions.store_relaxed(local_num_regions + 1); + + _num_initial_old_regions++; _g1h->old_set_remove(hr); } void G1CollectionSet::start() { - assert(_regions_cur_length == 0, "Collection set must be empty before starting a new collection set."); - assert(groups_cur_length() == 0, "Collection set groups must be empty before starting a new collection set."); - assert(_optional_groups.length() == 0, "Collection set optional gorups must be empty before starting a new collection set."); + assert(num_regions() == 0, "Collection set must be empty before starting a new collection set."); + assert(num_groups() == 0, "Collection set groups must be empty before starting a new collection set."); + assert(_optional_groups.length() == 0, + "Collection set optional groups must be empty before starting a new collection set."); continue_incremental_building(); @@ -149,8 +152,8 @@ void G1CollectionSet::start() { void G1CollectionSet::continue_incremental_building() { assert(_inc_build_state == CSetBuildType::Inactive, "Precondition"); - _regions_inc_part_start = _regions_cur_length; - _groups_inc_part_start = groups_cur_length(); + _regions_inc_part_start = num_regions(); + _groups_inc_part_start = num_groups(); DEBUG_ONLY(_inc_build_state = CSetBuildType::Active;) } @@ -161,14 +164,13 @@ void G1CollectionSet::stop_incremental_building() { void G1CollectionSet::clear() { assert_at_safepoint_on_vm_thread(); - _regions_cur_length = 0; + _num_regions.store_relaxed(0); _groups.clear(); assert(_optional_groups.length() == 0, "must be"); } void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { - size_t len = _regions_cur_length; - OrderAccess::loadload(); + uint len = _num_regions.load_acquire(); for (uint i = 0; i < len; i++) { G1HeapRegion* r = _g1h->region_at(_regions[i]); @@ -182,7 +184,7 @@ void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { void G1CollectionSet::par_iterate(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint worker_id) const { - iterate_part_from(cl, hr_claimer, 0, cur_length(), worker_id); + iterate_part_from(cl, hr_claimer, 0, num_regions(), worker_id); } void G1CollectionSet::iterate_optional(G1HeapRegionClosure* cl) const { @@ -197,13 +199,13 @@ void G1CollectionSet::iterate_optional(G1HeapRegionClosure* cl) const { void G1CollectionSet::iterate_incremental_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint worker_id) const { - iterate_part_from(cl, hr_claimer, _regions_inc_part_start, regions_cur_length(), worker_id); + iterate_part_from(cl, hr_claimer, _regions_inc_part_start, num_regions_in_increment(), worker_id); } void G1CollectionSet::iterate_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, - size_t offset, - size_t length, + uint offset, + uint length, uint worker_id) const { _g1h->par_iterate_regions_array(cl, hr_claimer, @@ -223,17 +225,16 @@ void G1CollectionSet::add_young_region_common(G1HeapRegion* hr) { // Synchronize with the region attribute table. _g1h->register_young_region_with_region_attr(hr); + uint index = num_regions(); // We use UINT_MAX as "invalid" marker in verification. - assert(_regions_cur_length < (UINT_MAX - 1), - "Collection set is too large with %u entries", _regions_cur_length); - hr->set_young_index_in_cset(_regions_cur_length + 1); + assert(index < (UINT_MAX - 1), "Collection set is too large with %u entries", index); + hr->set_young_index_in_cset(index + 1); - assert(_regions_cur_length < _regions_max_length, "Collection set larger than maximum allowed."); - _regions[_regions_cur_length] = hr->hrm_index(); + assert(index < _max_num_regions, "Collection set larger than maximum allowed."); + _regions[index] = hr->hrm_index(); // Concurrent readers must observe the store of the value in the array before an - // update to the length field. - OrderAccess::storestore(); - _regions_cur_length++; + // update to the _num_regions field. + _num_regions.fetch_then_add(1u, memory_order_release); } void G1CollectionSet::add_survivor_regions(G1HeapRegion* hr) { @@ -298,7 +299,7 @@ public: G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); _st->print_cr(" " HR_FORMAT ", TAMS: " PTR_FORMAT " PB: " PTR_FORMAT ", age: %4d", HR_FORMAT_PARAMS(r), - p2i(cm->top_at_mark_start(r)), + p2i(cm->top_at_mark_start_or_bottom(r)), p2i(r->parsable_bottom()), r->has_surv_rate_group() ? checked_cast(r->age_in_surv_rate_group()) : -1); return false; @@ -331,13 +332,13 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi log_trace(gc, ergo, cset)("Start choosing CSet. Pending cards: %zu target pause time: %1.2fms", pending_cards, target_pause_time_ms); - // The young list is laid with the survivor regions from the previous - // pause are appended to the RHS of the young list, i.e. - // [Newly Young Regions ++ Survivors from last pause]. + // Young region indexes are assigned with eden regions first, followed by + // survivor regions from the previous pause: + // [Eden regions ++ Survivors from last pause]. - uint eden_region_length = _g1h->eden_regions_count(); - uint survivor_region_length = survivors->length(); - init_region_lengths(eden_region_length, survivor_region_length); + uint num_eden_regions = _g1h->eden_regions_count(); + uint num_survivor_regions = survivors->length(); + prepare_for_collection(num_eden_regions, num_survivor_regions); verify_young_cset_indices(); @@ -345,16 +346,16 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi double predicted_base_time_ms = _policy->predict_base_time_ms(pending_cards, card_rs_length); // Base time already includes the whole remembered set related time, so do not add that here // again. - double predicted_eden_time = _policy->predict_young_region_other_time_ms(eden_region_length) + - _policy->predict_eden_copy_time_ms(eden_region_length); + double predicted_eden_time = _policy->predict_young_region_other_time_ms(num_eden_regions) + + _policy->predict_eden_copy_time_ms(num_eden_regions); double remaining_time_ms = MAX2(target_pause_time_ms - (predicted_base_time_ms + predicted_eden_time), 0.0); log_trace(gc, ergo, cset)("Added young regions to CSet. Eden: %u regions, Survivors: %u regions, " "predicted eden time: %1.2fms, predicted base time: %1.2fms, target pause time: %1.2fms, remaining time: %1.2fms", - eden_region_length, survivor_region_length, + num_eden_regions, num_survivor_regions, predicted_eden_time, predicted_base_time_ms, target_pause_time_ms, remaining_time_ms); - // Clear the fields that point to the survivor list - they are all young now. + // Set survivor regions as eden and clear survivor tracking for this pause. survivors->convert_to_eden(); phase_times()->record_young_cset_choice_time_ms((Ticks::now() - start_time).seconds() * 1000.0); @@ -425,7 +426,7 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); - bool check_time_remaining = _policy->use_adaptive_young_list_length(); + bool check_time_remaining = _policy->use_adaptive_num_young_regions(); G1CSetCandidateGroupList* from_marking_groups = &candidates()->from_marking_groups(); @@ -669,8 +670,8 @@ double G1CollectionSet::select_candidates_from_optional_groups(double time_remai } uint G1CollectionSet::select_optional_groups(double time_remaining_ms) { - uint optional_regions_count = num_optional_regions(); - assert(optional_regions_count > 0, + uint total_optional_regions = num_optional_regions(); + assert(total_optional_regions > 0, "Should only be called when there are optional regions"); uint num_regions_selected = 0; @@ -678,7 +679,7 @@ uint G1CollectionSet::select_optional_groups(double time_remaining_ms) { double total_prediction_ms = select_candidates_from_optional_groups(time_remaining_ms, num_regions_selected); log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", - num_regions_selected, optional_regions_count, total_prediction_ms); + num_regions_selected, total_optional_regions, total_prediction_ms); return num_regions_selected; } @@ -754,13 +755,12 @@ void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* p #ifdef ASSERT class G1VerifyYoungCSetIndicesClosure : public G1HeapRegionClosure { -private: - size_t _young_length; + uint _num_young_regions; uint* _heap_region_indices; public: - G1VerifyYoungCSetIndicesClosure(size_t young_length) : G1HeapRegionClosure(), _young_length(young_length) { - _heap_region_indices = NEW_C_HEAP_ARRAY(uint, young_length + 1, mtGC); - for (size_t i = 0; i < young_length + 1; i++) { + G1VerifyYoungCSetIndicesClosure(uint num_young_regions) : G1HeapRegionClosure(), _num_young_regions(num_young_regions) { + _heap_region_indices = NEW_C_HEAP_ARRAY(uint, num_young_regions + 1, mtGC); + for (uint i = 0; i < num_young_regions + 1; i++) { _heap_region_indices[i] = UINT_MAX; } } @@ -771,8 +771,9 @@ public: virtual bool do_heap_region(G1HeapRegion* r) { const uint idx = r->young_index_in_cset(); - assert(idx > 0, "Young index must be set for all regions in the incremental collection set but is not for region %u.", r->hrm_index()); - assert(idx <= _young_length, "Young cset index %u too large for region %u", idx, r->hrm_index()); + assert(r->is_young(), "must be, but region %u is not", r->hrm_index()); + assert(idx > 0, "Young index must be set for all regions in the collection set but is not for region %u.", r->hrm_index()); + assert(idx <= _num_young_regions, "Young cset index %u too large for region %u", idx, r->hrm_index()); assert(_heap_region_indices[idx] == UINT_MAX, "Index %d used by multiple regions, first use by region %u, second by region %u", @@ -787,7 +788,7 @@ public: void G1CollectionSet::verify_young_cset_indices() const { assert_at_safepoint_on_vm_thread(); - G1VerifyYoungCSetIndicesClosure cl(_regions_cur_length); + G1VerifyYoungCSetIndicesClosure cl(num_regions()); iterate(&cl); } #endif diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index df0228c4956..eee985f259d 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.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 @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1COLLECTIONSET_HPP #include "gc/g1/g1CollectionSetCandidates.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -139,26 +140,26 @@ class G1CollectionSet { // The actual collection set as a set of region indices. // - // All regions in _regions below _regions_cur_length are assumed to be part of the + // All regions in _regions below _num_regions are assumed to be part of the // collection set. // We assume that at any time there is at most only one writer and (one or more) - // concurrent readers. This means synchronization using storestore and loadload - // barriers on the writer and reader respectively only are sufficient. + // concurrent readers. This means synchronization using release and acquire + // on the writer and reader respectively only are sufficient. // // This corresponds to the regions referenced by the candidate groups further below. uint* _regions; - uint _regions_max_length; + uint _max_num_regions; - volatile uint _regions_cur_length; + Atomic _num_regions; // Old gen groups selected for evacuation. G1CSetCandidateGroupList _groups; - uint groups_cur_length() const; + uint num_groups() const; - uint _eden_region_length; - uint _survivor_region_length; - uint _initial_old_region_length; + uint _num_eden_regions; + uint _num_survivor_regions; + uint _num_initial_old_regions; // When doing mixed collections we can add old regions to the collection set, which // will be collected only if there is enough time. We call these optional (old) @@ -174,7 +175,7 @@ class G1CollectionSet { CSetBuildType _inc_build_state; #endif // Index into the _regions indicating the start of the current collection set increment. - size_t _regions_inc_part_start; + uint _regions_inc_part_start; // Index into the _groups indicating the start of the current collection set increment. uint _groups_inc_part_start; @@ -188,6 +189,9 @@ class G1CollectionSet { // Add the given old region to the current collection set. void add_old_region(G1HeapRegion* hr); + void prepare_for_collection(uint num_eden_cset_regions, + uint num_survivor_cset_regions); + void prepare_optional_group(G1CSetCandidateGroup* gr, uint cur_index); void add_group_to_collection_set(G1CSetCandidateGroup* gr); @@ -201,7 +205,7 @@ class G1CollectionSet { // Select groups for evacuation from the optional candidates given the remaining time // and return the number of actually selected regions. uint select_optional_groups(double time_remaining_ms); - double select_candidates_from_optional_groups(double time_remaining_ms, uint& num_groups_selected); + double select_candidates_from_optional_groups(double time_remaining_ms, uint& num_regions_selected); // Finalize the young part of the initial collection set. Relabel survivor regions // as Eden and calculate a prediction on how long the evacuation of all young regions @@ -217,8 +221,8 @@ class G1CollectionSet { // to allow for more efficient parallel iteration. void iterate_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, - size_t offset, - size_t length, + uint offset, + uint length, uint worker_id) const; // Adds the given group to the optional groups list (_optional_groups) @@ -232,8 +236,8 @@ public: G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy); ~G1CollectionSet(); - // Initializes the collection set giving the maximum possible length of the collection set. - void initialize(uint max_region_length); + // Initializes the collection set giving the maximum possible number of regions in the collection set. + void initialize(uint max_num_regions); // Drop the collection set and collection set candidates. void abandon(); @@ -245,26 +249,23 @@ public: void prepare_for_scan(); - void init_region_lengths(uint eden_cset_region_length, - uint survivor_cset_region_length); + // Total number of regions in the initial collection set. + uint num_initial_regions() const { return num_young_regions() + + num_initial_old_regions(); } + uint num_young_regions() const { return num_eden_regions() + + num_survivor_regions(); } - // Total length of the initial collection set in regions. - uint initial_region_length() const { return young_region_length() + - initial_old_region_length(); } - uint young_region_length() const { return eden_region_length() + - survivor_region_length(); } - - uint eden_region_length() const { return _eden_region_length; } - uint survivor_region_length() const { return _survivor_region_length; } - uint initial_old_region_length() const { return _initial_old_region_length; } + uint num_eden_regions() const { return _num_eden_regions; } + uint num_survivor_regions() const { return _num_survivor_regions; } + uint num_initial_old_regions() const { return _num_initial_old_regions; } uint num_optional_regions() const { return _optional_groups.num_regions(); } - bool only_contains_young_regions() const { return (initial_old_region_length() + num_optional_regions()) == 0; } + bool only_contains_young_regions() const { return (num_initial_old_regions() + num_optional_regions()) == 0; } template inline void merge_cardsets_for_collection_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers); - uint groups_increment_length() const; + uint num_groups_in_increment() const; // Reset the contents of the collection set. void clear(); @@ -282,10 +283,10 @@ public: // from a starting position determined by the given worker id. void iterate_incremental_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint worker_id) const; - // Returns the length of the current increment in number of regions. - size_t regions_cur_length() const { return _regions_cur_length - _regions_inc_part_start; } - // Returns the length of the whole current collection set in number of regions - size_t cur_length() const { return _regions_cur_length; } + // Returns the number of regions in the current collection set increment. + uint num_regions_in_increment() const { return num_regions() - _regions_inc_part_start; } + // Returns the total number of regions in the current collection set. + uint num_regions() const { return _num_regions.load_relaxed(); } // Iterate over the entire collection set (all increments calculated so far), applying // the given G1HeapRegionClosure on all of the regions. diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp index b0321588d18..f9ec42b9432 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.inline.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 @@ -31,24 +31,22 @@ template inline void G1CollectionSet::merge_cardsets_for_collection_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers) { - uint offset = _groups_inc_part_start; + uint offset = _groups_inc_part_start; if (offset == 0) { G1HeapRegionRemSet::iterate_for_merge(_g1h->young_regions_cset_group()->card_set(), cl); } - uint next_increment_length = groups_increment_length(); - if (next_increment_length == 0) { + const uint next_group_increment = num_groups_in_increment(); + if (next_group_increment == 0) { return; } - uint start_pos = (worker_id * next_increment_length) / num_workers; + uint start_pos = (worker_id * next_group_increment) / num_workers; uint cur_pos = start_pos; - uint count = 0; do { G1HeapRegionRemSet::iterate_for_merge(_groups.at(offset + cur_pos)->card_set(), cl); cur_pos++; - count++; - if (cur_pos == next_increment_length) { + if (cur_pos == next_group_increment) { cur_pos = 0; } } while (cur_pos != start_pos); diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 3637d477229..ac1b29a6bd7 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -134,7 +134,7 @@ void G1CSetCandidateGroupList::append(G1CSetCandidateGroup* group) { assert(group->length() > 0, "Do not add empty groups"); assert(!_groups.contains(group), "Already added to list"); _groups.append(group); - _num_regions += group->length(); + _num_regions.store_relaxed(num_regions() + group->length()); } G1CSetCandidateGroup* G1CSetCandidateGroupList::at(uint index) { @@ -147,7 +147,7 @@ void G1CSetCandidateGroupList::clear(bool uninstall_group_cardset) { delete gr; } _groups.clear(); - _num_regions = 0; + _num_regions.store_relaxed(0); } void G1CSetCandidateGroupList::prepare_for_scan() { @@ -156,9 +156,9 @@ void G1CSetCandidateGroupList::prepare_for_scan() { } } -void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions) { +void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions_to_remove) { _groups.remove_till(count); - _num_regions -= num_regions; + _num_regions.store_relaxed(num_regions() - num_regions_to_remove); } void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { @@ -172,7 +172,7 @@ void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { // Create a list from scratch, copying over the elements from the candidate // list not in the other list. Finally deallocate and overwrite the old list. int new_length = _groups.length() - other->length(); - _num_regions = num_regions() - other->num_regions(); + _num_regions.store_relaxed(num_regions() - other->num_regions()); GrowableArray new_list(new_length, mtGC); uint other_idx = 0; diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 8a2235cf89c..a70f9e395b6 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -29,6 +29,7 @@ #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/shared/gc_globals.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" @@ -147,7 +148,7 @@ using G1CSetCandidateGroupListIterator = GrowableArrayIterator _groups; - volatile uint _num_regions; + Atomic _num_regions; public: G1CSetCandidateGroupList(); @@ -163,7 +164,7 @@ public: uint length() const { return (uint)_groups.length(); } - uint num_regions() const { return _num_regions; } + uint num_regions() const { return _num_regions.load_relaxed(); } void remove_selected(uint count, uint num_regions); diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index 762ddb1fc8f..002b7894030 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -59,7 +59,7 @@ class G1CollectorState { // has been in progress when the request came in. // // This flag remembers that there is an unfullfilled request. - volatile bool _initiate_conc_mark_if_possible; + bool _initiate_conc_mark_if_possible; public: G1CollectorState() : diff --git a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp index b2d3dfcc489..1a0e91f1adb 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp @@ -42,7 +42,7 @@ inline void G1CollectorState::set_in_full_gc() { inline void G1CollectorState::set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; - _initiate_conc_mark_if_possible = false; + set_initiate_conc_mark_if_possible(false); } inline void G1CollectorState::set_in_prepare_mixed_gc() { _phase = Phase::YoungPrepareMixed; @@ -77,19 +77,22 @@ inline bool G1CollectorState::initiate_conc_mark_if_possible() const { inline bool G1CollectorState::is_in_concurrent_cycle() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_concurrent_cycle(); + return cm->is_fully_initialized() && cm->is_in_concurrent_cycle(); } + inline bool G1CollectorState::is_in_marking() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_marking(); + return cm->is_fully_initialized() && 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(); + return cm->is_fully_initialized() && cm->is_in_marking_or_rebuild(); } + inline bool G1CollectorState::is_in_reset_for_next_cycle() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_reset_for_next_cycle(); + return cm->is_fully_initialized() && cm->is_in_reset_for_next_cycle(); } inline void G1CollectorState::assert_is_young_pause(Pause type) { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 4afc7fa8ff1..73a697f8c51 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -24,6 +24,7 @@ #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataGraph.hpp" +#include "code/codeCache.hpp" #include "cppstdlib/new.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BatchedTask.hpp" @@ -299,7 +300,7 @@ void G1CMMarkStack::add_chunk_to_list(Atomic* list, TaskQu void G1CMMarkStack::add_chunk_to_chunk_list(TaskQueueEntryChunk* elem) { MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); add_chunk_to_list(&_chunk_list, elem); - _chunks_in_chunk_list++; + _chunks_in_chunk_list.add_then_fetch(1u, memory_order_relaxed); } void G1CMMarkStack::add_chunk_to_free_list(TaskQueueEntryChunk* elem) { @@ -319,7 +320,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_chunk_list( MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); TaskQueueEntryChunk* result = remove_chunk_from_list(&_chunk_list); if (result != nullptr) { - _chunks_in_chunk_list--; + _chunks_in_chunk_list.sub_then_fetch(1u, memory_order_relaxed); } return result; } @@ -363,7 +364,7 @@ bool G1CMMarkStack::par_pop_chunk(G1TaskQueueEntry* ptr_arr) { } void G1CMMarkStack::set_empty() { - _chunks_in_chunk_list = 0; + _chunks_in_chunk_list.store_relaxed(0); _chunk_list.store_relaxed(nullptr); _free_list.store_relaxed(nullptr); _chunk_allocator.reset(); @@ -449,7 +450,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _worker_id_offset(G1ConcRefinementThreads), // The refinement control thread does not refine cards, so it's just the worker threads. _max_num_tasks(MAX2(ConcGCThreads, ParallelGCThreads)), _num_active_tasks(0), // _num_active_tasks set in set_non_marking_state() - _tasks(nullptr), // _tasks set inside late_init() + _tasks(nullptr), _task_queues(new G1CMTaskQueueSet(_max_num_tasks)), _terminator(_max_num_tasks, _task_queues), _partial_array_state_manager(new PartialArrayStateManager(_max_num_tasks)), @@ -476,9 +477,10 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _num_concurrent_workers(0), _max_concurrent_workers(0), - _region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC)), - _top_at_mark_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), - _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), + _is_region_mark_stats_cache_in_use(false), + _region_mark_stats(nullptr), + _top_at_mark_starts(nullptr), + _top_at_rebuild_starts(nullptr), _needs_remembered_set_rebuild(false) { assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); @@ -487,6 +489,8 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, } void G1ConcurrentMark::fully_initialize() { + assert_at_safepoint(); + if (is_fully_initialized()) { return; } @@ -510,6 +514,10 @@ void G1ConcurrentMark::fully_initialize() { vm_exit_during_initialization("Failed to allocate initial concurrent mark overflow mark stack."); } + _region_mark_stats = NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC); + _top_at_mark_starts = NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC); + _top_at_rebuild_starts = NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC); + _tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_num_tasks, mtGC); // so that the assertion in MarkingTaskQueue::task_queue doesn't fail @@ -527,22 +535,21 @@ void G1ConcurrentMark::fully_initialize() { for (uint i = 0; i < max_num_regions; i++) { ::new (&_top_at_mark_starts[i]) Atomic(_g1h->bottom_addr_for_region(i)); } - // Contrary to TAMS, the default value of _top_at_rebuild_starts needs to be null. ::new (_top_at_rebuild_starts) Atomic[max_num_regions]{}; reset_at_marking_complete(); } bool G1ConcurrentMark::is_in_concurrent_cycle() const { - return is_fully_initialized() ? _cm_thread->is_in_progress() : false; + return _cm_thread->is_in_progress(); } bool G1ConcurrentMark::is_in_marking() const { - return is_fully_initialized() ? cm_thread()->is_in_marking() : false; + return _cm_thread->is_in_marking(); } -bool G1ConcurrentMark::is_in_rebuild_or_scrub() const { - return cm_thread()->is_in_rebuild_or_scrub(); +bool G1ConcurrentMark::is_in_marking_or_rebuild() const { + return _cm_thread->is_in_marking_or_rebuild(); } bool G1ConcurrentMark::is_in_reset_for_next_cycle() const { @@ -559,8 +566,11 @@ G1ConcurrentMarkThread* G1ConcurrentMark::cm_thread() const { } void G1ConcurrentMark::reset() { + assert_fully_initialized(); + _has_aborted.store_relaxed(false); + _is_region_mark_stats_cache_in_use = true; reset_marking_for_restart(); // Reset all tasks, since different phases will use different number of active @@ -572,6 +582,9 @@ void G1ConcurrentMark::reset() { uint max_num_regions = _g1h->max_num_regions(); ::new (_top_at_rebuild_starts) Atomic[max_num_regions]{}; for (uint i = 0; i < max_num_regions; i++) { + // Do not update TAMS here. NoteStartOfMarkTask updates this in parallel in + // the pre-concurrent-start WorkerTask. + _top_at_rebuild_starts[i].store_relaxed(nullptr); _region_mark_stats[i].clear(); } @@ -579,11 +592,61 @@ void G1ConcurrentMark::reset() { _root_regions.reset(); } -void G1ConcurrentMark::clear_statistics(G1HeapRegion* r) { +void G1ConcurrentMark::assert_statistics_clear(G1HeapRegion* r) { + assert_fully_initialized(); +#ifdef ASSERT uint region_idx = r->hrm_index(); for (uint j = 0; j < _max_num_tasks; ++j) { - _tasks[j]->clear_mark_stats_cache(region_idx); + _tasks[j]->verify_no_mark_stats_for(r->hrm_index()); } + + assert(_top_at_rebuild_starts[region_idx].load_relaxed() == nullptr, "must be"); + + G1RegionMarkStats* s = &_region_mark_stats[region_idx]; + assert(s->incoming_refs() == 0, "must be"); + assert(s->live_words() == 0, "must be"); +#endif +} + +void G1ConcurrentMark::note_start_of_mark_for_region(G1HeapRegion* r) { + assert_at_safepoint(); + assert_fully_initialized(); + if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) { + update_top_at_mark_start(r); + } else { + set_top_at_mark_start_to_bottom(r); + } +} + +void G1ConcurrentMark::notify_new_region(G1HeapRegion* r, size_t marked_live_bytes_below_tams) { + assert_at_safepoint(); + if (!is_fully_initialized()) { + return; + } + G1CollectorState* state = _g1h->collector_state(); + if (state->is_in_concurrent_start_gc()) { + update_top_at_mark_start(r); + set_live_bytes(r->hrm_index(), marked_live_bytes_below_tams); + } +} + +void G1ConcurrentMark::reset_region_marking_state(G1HeapRegion* r) { + assert_at_safepoint(); + if (!is_fully_initialized()) { + return; + } + uint region_idx = r->hrm_index(); + // Only need to clear the stats cache for the given region if we are using the cache. + if (_is_region_mark_stats_cache_in_use) { + for (uint j = 0; j < _max_num_tasks; ++j) { + _tasks[j]->clear_mark_stats_cache(region_idx); + } + } else { + for (uint j = 0; j < _max_num_tasks; ++j) { + _tasks[j]->verify_no_mark_stats_for(region_idx); + } + } + set_top_at_mark_start_to_bottom(r); _top_at_rebuild_starts[region_idx].store_relaxed(nullptr); _region_mark_stats[region_idx].clear(); } @@ -594,19 +657,11 @@ void G1ConcurrentMark::humongous_object_eagerly_reclaimed(G1HeapRegion* r) { // Need to clear mark bit of the humongous object. Doing this unconditionally is fine. mark_bitmap()->clear(r->bottom()); - - if (!_g1h->collector_state()->is_in_mark_or_rebuild()) { - return; - } - - // Clear any statistics about the region gathered so far. - _g1h->humongous_obj_regions_iterate(r, - [&] (G1HeapRegion* r) { - clear_statistics(r); - }); } void G1ConcurrentMark::reset_marking_for_restart() { + assert_fully_initialized(); + _global_mark_stack.set_empty(); // Expand the marking stack, if we have to and if we can. @@ -729,7 +784,6 @@ private: assert(_bitmap->get_next_marked_addr(r->bottom(), r->end()) == r->end(), "Should not have marked bits"); return r->bottom(); } - assert(_bitmap->get_next_marked_addr(_cm->top_at_mark_start(r), r->end()) == r->end(), "Should not have marked bits above tams"); } return r->end(); } @@ -776,8 +830,6 @@ private: } assert(cur >= end, "Must have completed iteration over the bitmap for region %u.", r->hrm_index()); - _cm->reset_top_at_mark_start(r); - return false; } }; @@ -877,11 +929,7 @@ class G1PreConcurrentStartTask::NoteStartOfMarkTask : public G1AbstractSubTask { NoteStartOfMarkHRClosure() : G1HeapRegionClosure(), _cm(G1CollectedHeap::heap()->concurrent_mark()) { } bool do_heap_region(G1HeapRegion* r) override { - if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) { - _cm->update_top_at_mark_start(r); - } else { - _cm->reset_top_at_mark_start(r); - } + _cm->note_start_of_mark_for_region(r); return false; } } _region_cl; @@ -1189,6 +1237,11 @@ void G1ConcurrentMark::add_root_region(G1HeapRegion* r) { root_regions()->add(top_at_mark_start(r), r->top()); } +void G1ConcurrentMark::add_root_region_set_bottom(G1HeapRegion* r) { + set_top_at_mark_start_to_bottom(r); + root_regions()->add(r->bottom(), r->top()); +} + bool G1ConcurrentMark::is_root_region(G1HeapRegion* r) { return root_regions()->contains(MemRegion(top_at_mark_start(r), r->top())); } @@ -1326,6 +1379,8 @@ void G1ConcurrentMark::remark() { if (mark_finished) { weak_refs_work(); + CodeCache::on_gc_marking_cycle_finish(); + // Unload Klasses, String, Code Cache, etc. if (ClassUnloadingWithConcurrentMark) { G1CMIsAliveClosure is_alive(this); @@ -1394,7 +1449,7 @@ void G1ConcurrentMark::remark() { // Completely reset the marking state (except bitmaps) since marking completed. reset_at_marking_complete(); - G1CollectedHeap::finish_codecache_marking_cycle(); + CodeCache::arm_all_nmethods(); { GCTraceTime(Debug, gc, phases) debug("Report Object Count", _gc_timer_cm); @@ -1822,7 +1877,7 @@ void G1ConcurrentMark::finalize_marking() { print_stats(); } -void G1ConcurrentMark::flush_all_task_caches() { +void G1ConcurrentMark::flush_all_task_caches(bool ends_use_of_mark_cache) { size_t hits = 0; size_t misses = 0; for (uint i = 0; i < _max_num_tasks; i++) { @@ -1833,6 +1888,9 @@ void G1ConcurrentMark::flush_all_task_caches() { size_t sum = hits + misses; log_debug(gc, stats)("Mark stats cache hits %zu misses %zu ratio %1.3lf", hits, misses, percent_of(hits, sum)); + if (ends_use_of_mark_cache) { + _is_region_mark_stats_cache_in_use = false; + } } void G1ConcurrentMark::clear_bitmap_for_region(G1HeapRegion* hr) { @@ -1870,7 +1928,8 @@ G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) { return curr_region; } else { assert(limit == bottom, - "The region limit should be at bottom"); + "The scan limit for region %u (%s) should be bottom but is " PTR_FORMAT, + curr_region->hrm_index(), curr_region->get_short_type_str(), p2i(limit)); // We return null and the caller should try calling // claim_region() again. return nullptr; @@ -1878,7 +1937,9 @@ G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) { } else { // Read the finger again. HeapWord* next_finger = finger(); - assert(next_finger > local_finger, "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, p2i(local_finger), p2i(next_finger)); + assert(next_finger > local_finger, + "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, + p2i(local_finger), p2i(next_finger)); local_finger = next_finger; } } @@ -1978,6 +2039,26 @@ void G1ConcurrentMark::print_stats() { } } +bool G1ConcurrentMark::shutdown_cleanup_needed() const { + // Cleanup (aborting threads, setting abort flags) is needed throughout the whole cycle before + // stopping the CM thread. + return is_fully_initialized() && is_in_concurrent_cycle(); +} + +void G1ConcurrentMark::shutdown_concurrent_cycle() { + assert_at_safepoint_on_vm_thread(); + + abort_root_region_scan_at_safepoint(); + abort_marking_threads(); + + SATBMarkQueueSet& satb_mq_set = G1BarrierSet::satb_mark_queue_set(); + satb_mq_set.abandon_partial_marking(); + // This can be called either during or outside marking, we'll read + // the expected_active value from the SATB queue set. + satb_mq_set.set_active_all_threads(false, /* new active value */ + satb_mq_set.is_active() /* expected_active */); +} + bool G1ConcurrentMark::concurrent_cycle_abort() { assert_at_safepoint_on_vm_thread(); assert(_g1h->collector_state()->is_in_full_gc(), "must be"); @@ -2003,6 +2084,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { return false; } + flush_all_task_caches(); reset_marking_for_restart(); abort_marking_threads(); @@ -2457,6 +2539,12 @@ void G1CMTask::drain_satb_buffers() { decrease_limits(); } +#ifndef PRODUCT +void G1CMTask::verify_no_mark_stats_for(uint region_idx) { + _mark_stats_cache.verify_no_mark_stats_for(region_idx); +} +#endif + void G1CMTask::clear_mark_stats_cache(uint region_idx) { _mark_stats_cache.reset(region_idx); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 1ab4654a490..f1f84bf246e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -213,8 +213,8 @@ private: Atomic _free_list; // Linked list of free chunks that can be allocated by users. char _pad1[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*)]; Atomic _chunk_list; // List of chunks currently containing data. - volatile size_t _chunks_in_chunk_list; - char _pad2[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(size_t)]; + Atomic _chunks_in_chunk_list; + char _pad2[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(_chunks_in_chunk_list)]; // Atomically add the given chunk to the list. void add_chunk_to_list(Atomic* list, TaskQueueEntryChunk* elem); @@ -265,7 +265,7 @@ private: // Return the approximate number of oops on this mark stack. Racy due to // unsynchronized access to _chunks_in_chunk_list. - size_t size() const { return _chunks_in_chunk_list * EntriesPerChunk; } + size_t size() const { return _chunks_in_chunk_list.load_relaxed() * EntriesPerChunk; } void set_empty(); @@ -323,6 +323,7 @@ public: // This class manages data structures and methods for doing liveness analysis in // G1's concurrent cycle. class G1ConcurrentMark : public CHeapObj { + friend class G1ClearBitMapTask; friend class G1CMBitMapClosure; friend class G1CMConcurrentMarkingTask; friend class G1CMDrainMarkingStackClosure; @@ -331,7 +332,6 @@ class G1ConcurrentMark : public CHeapObj { friend class G1CMRemarkTask; friend class G1CMRootRegionScanTask; friend class G1CMTask; - friend class G1ClearBitMapTask; friend class G1CollectorState; friend class G1ConcurrentMarkThread; @@ -494,15 +494,18 @@ class G1ConcurrentMark : public CHeapObj { // true, periodically insert checks to see if this method should exit prematurely. void clear_bitmap(WorkerThreads* workers, bool may_yield); + // Records whether the region mark stats cache may contain entries due to marking activity, + // and the cache for freed regions needs to be cleared for those. + bool _is_region_mark_stats_cache_in_use; // Region statistics gathered during marking. G1RegionMarkStats* _region_mark_stats; - // Top pointer for each region at the start of marking. Must be valid for all committed - // regions. + // Top pointer for each region at the start of marking. Must be valid, i.e. be within + // [bottom, end] of a region for all committed regions. Atomic* _top_at_mark_starts; // Top pointer for each region at the start of the rebuild remembered set process // for regions which remembered sets need to be rebuilt. A null for a given region - // means that this region does not be scanned during the rebuilding remembered - // set phase at all. + // means that this region does not need to be scanned during the remembered set rebuild + // phase at all. Atomic* _top_at_rebuild_starts; // True when Remark pause selected regions for rebuilding. bool _needs_remembered_set_rebuild; @@ -512,27 +515,47 @@ class G1ConcurrentMark : public CHeapObj { // Concurrent cycle state queries. bool is_in_concurrent_cycle() const; bool is_in_marking() const; - bool is_in_rebuild_or_scrub() const; + bool is_in_marking_or_rebuild() const; bool is_in_reset_for_next_cycle() const; + void assert_fully_initialized() const { assert(is_fully_initialized(), "must be"); } + // The TAMS may be read and returns useful values related to the current concurrent marking. + // This is the case only during the concurrent cycle or the Concurrent Start pause. + inline bool tams_may_be_read() const; + // Update the TAMS for the given region to the current top. + inline void update_top_at_mark_start(G1HeapRegion* r); + // Reset the TAMS for the given region to bottom. + inline void set_top_at_mark_start_to_bottom(G1HeapRegion* r); + public: // To be called when an object is marked the first time, e.g. after a successful // mark_in_bitmap call. Updates various statistics data. void add_to_liveness(uint worker_id, oop const obj, size_t size); // Did the last marking find a live object between bottom and TAMS? - bool contains_live_object(uint region) const { return _region_mark_stats[region].live_words() != 0; } + bool contains_live_object(uint region) const; // Live bytes in the given region as determined by concurrent marking, i.e. the amount of // live bytes between bottom and TAMS. - size_t live_bytes(uint region) const { return _region_mark_stats[region].live_words() * HeapWordSize; } + size_t live_bytes(uint region) const; // Set live bytes for concurrent marking. - void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words.store_relaxed(live_bytes / HeapWordSize); } + void set_live_bytes(uint region, size_t live_bytes); // Approximate number of incoming references found during marking. - size_t incoming_refs(uint region) const { return _region_mark_stats[region].incoming_refs(); } + size_t incoming_refs(uint region) const; - // Update the TAMS for the given region to the current top. - inline void update_top_at_mark_start(G1HeapRegion* r); - // Reset the TAMS for the given region to bottom of that region. - inline void reset_top_at_mark_start(G1HeapRegion* r); + void note_start_of_mark_for_region(G1HeapRegion* r); + inline void assert_top_at_mark_start_is_bottom(G1HeapRegion* r); + + // Returns the TAMS for the given region; outside of the concurrent cycle or Concurrent Start + // pause, always returns r->bottom(). + // Intended to be used for queries that are not allowed to fail at any time, but give a + // reasonable value, e.g. for logging to avoid having to do lots of check at every call site. + // Do not use for logic. + inline HeapWord* top_at_mark_start_or_bottom(const G1HeapRegion* r) const; + // Special method to return TAMS for verification purposes. During verification, if Full GC + // aborted a concurrent cycle, we need to use the TAMS data because the bitmap < TAMS may + // legitimately contain marks, however since we are in a Full GC tams_may_be_read() returns + // false. The other methods would return bottom(), which is wrong for verification. + inline HeapWord* top_at_mark_start_for_verification(const G1HeapRegion* r, + bool concurrent_cycle_aborted) const; inline HeapWord* top_at_mark_start(const G1HeapRegion* r) const; inline HeapWord* top_at_mark_start(uint region) const; @@ -546,15 +569,26 @@ public: uint worker_id_offset() const { return _worker_id_offset; } + // Fully allocates and initializes data structures for the concurrent cycle. + // Methods that use concurrent cycle state such as the concurrent mark threads, + // tasks, marking stack, statistics, TAMS or TARS require this initialization. + // Callers that run before the first concurrent start pause, which calls this, + // should guard calls with is_fully_initialized(). void fully_initialize(); bool is_fully_initialized() const { return _cm_thread != nullptr; } uint max_num_tasks() const {return _max_num_tasks; } - // Clear statistics gathered during the concurrent cycle for the given region after - // it has been reclaimed. - void clear_statistics(G1HeapRegion* r); - // Notification for eagerly reclaimed regions to clean up. + void assert_statistics_clear(G1HeapRegion* r); + + // Notification for marking that a new region has been added to the heap. Updates the TAMS and + // live bytes for this region during a Concurrent Start pause. + void notify_new_region(G1HeapRegion* r, size_t marked_live_bytes_below_tams = 0); + + // Resets region marking state for the given region, i.e. TAMS, statistics, task metadata, + // etc. to initial state. + void reset_region_marking_state(G1HeapRegion* r); + // Notification for eagerly reclaimed regions to do extra clean up. void humongous_object_eagerly_reclaimed(G1HeapRegion* r); // Manipulation of the global mark stack. // The push and pop operations are used by tasks for transfers @@ -574,6 +608,8 @@ public: bool mark_stack_empty() const { return _global_mark_stack.is_empty(); } void concurrent_cycle_start(); + bool shutdown_cleanup_needed() const; + void shutdown_concurrent_cycle(); // Abandon current marking iteration due to a Full GC. bool concurrent_cycle_abort(); void concurrent_cycle_end(bool mark_cycle_completed); @@ -605,7 +641,7 @@ public: void reset(); // Moves all per-task cached data into global state. - void flush_all_task_caches(); + void flush_all_task_caches(bool ends_use_of_mark_cache = true); // Prepare internal data structures for the next mark cycle. This includes clearing // the next mark bitmap and some internal data structures. This method is intended // to be called concurrently to the mutator. It will yield to safepoint requests. @@ -632,6 +668,7 @@ public: void stop(); void add_root_region(G1HeapRegion* r); + void add_root_region_set_bottom(G1HeapRegion* r); bool is_root_region(G1HeapRegion* r); // Scan all the root regions concurrently and mark everything reachable from @@ -958,6 +995,7 @@ public: inline void inc_incoming_refs(oop const obj); + void verify_no_mark_stats_for(uint region_idx) PRODUCT_RETURN; // Clear (without flushing) the mark cache entry for the given region. void clear_mark_stats_cache(uint region_idx); // Evict the whole statistics cache into the global statistics. Returns the diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index 094f4dca994..76fdcd218ae 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" @@ -92,7 +93,7 @@ inline void G1CMMarkStack::iterate(Fn fn) const { TaskQueueEntryChunk* cur = _chunk_list.load_relaxed(); while (cur != nullptr) { - guarantee(num_chunks <= _chunks_in_chunk_list, "Found %zu oop chunks which is more than there should be", num_chunks); + guarantee(num_chunks <= _chunks_in_chunk_list.load_relaxed(), "Found %zu oop chunks which is more than there should be", num_chunks); for (size_t i = 0; i < EntriesPerChunk; ++i) { if (cur->data[i].is_null()) { @@ -189,20 +190,69 @@ inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t } inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) { + assert_fully_initialized(); + assert(_g1h->collector_state()->is_in_concurrent_start_gc(), "must be"); uint const region = r->hrm_index(); assert(region < _g1h->max_num_regions(), "Tried to access TAMS for region %u out of bounds", region); _top_at_mark_starts[region].store_relaxed(r->top()); } -inline void G1ConcurrentMark::reset_top_at_mark_start(G1HeapRegion* r) { +inline void G1ConcurrentMark::set_top_at_mark_start_to_bottom(G1HeapRegion* r) { + assert_fully_initialized(); _top_at_mark_starts[r->hrm_index()].store_relaxed(r->bottom()); } +inline void G1ConcurrentMark::assert_top_at_mark_start_is_bottom(G1HeapRegion* r) { + // Can not assert anything if not initialized. + if (!tams_may_be_read()) { + return; + } + HeapWord* local_top_at_mark_start = top_at_mark_start(r); + assert(local_top_at_mark_start == r->bottom(), + "must be, but tams for r %u (%s) is" PTR_FORMAT, + r->hrm_index(), r->get_short_type_str(), p2i(local_top_at_mark_start)); +} + +inline HeapWord* G1ConcurrentMark::top_at_mark_start_or_bottom(const G1HeapRegion* r) const { + if (!tams_may_be_read()) { + return r->bottom(); + } + return top_at_mark_start(r); +} + +inline HeapWord* G1ConcurrentMark::top_at_mark_start_for_verification(const G1HeapRegion* r, + bool concurrent_cycle_aborted) const { + if (!is_fully_initialized()) { + // We do not have TAMS data yet. + return r->bottom(); + } + if (tams_may_be_read()) { + // Normal case, we can read TAMS data and it is valid. + return top_at_mark_start(r); + } + if (concurrent_cycle_aborted) { + assert(_g1h->collector_state()->is_in_full_gc(), "Must be in Full GC if concurrent cycle has aborted"); + assert(r->hrm_index() < _g1h->max_num_regions(), + "Tried to access TAMS for region %u out of bounds", r->hrm_index()); + return _top_at_mark_starts[r->hrm_index()].load_relaxed(); + } + return r->bottom(); +} + +inline bool G1ConcurrentMark::tams_may_be_read() const { + // We need the TAMS to be valid even outside of actual marking for e.g. clearing the bitmap. + G1CollectorState* state = _g1h->collector_state(); + return is_fully_initialized() && + (state->is_in_concurrent_cycle() || state->is_in_concurrent_start_gc()); +} + inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) const { return top_at_mark_start(r->hrm_index()); } inline HeapWord* G1ConcurrentMark::top_at_mark_start(uint region) const { + assert_fully_initialized(); + assert(tams_may_be_read(), "must be"); assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region); return _top_at_mark_starts[region].load_relaxed(); } @@ -214,10 +264,12 @@ inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const { } inline HeapWord* G1ConcurrentMark::top_at_rebuild_start(G1HeapRegion* r) const { + assert_fully_initialized(); return _top_at_rebuild_starts[r->hrm_index()].load_relaxed(); } inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) { + assert_fully_initialized(); assert(r->is_old() || r->is_humongous(), "precondition"); uint const region = r->hrm_index(); @@ -240,6 +292,26 @@ inline void G1ConcurrentMark::add_to_liveness(uint worker_id, oop const obj, siz task(worker_id)->update_liveness(obj, size); } +inline bool G1ConcurrentMark::contains_live_object(uint region) const { + assert_fully_initialized(); + return _region_mark_stats[region].live_words() != 0; +} + +inline size_t G1ConcurrentMark::live_bytes(uint region) const { + assert_fully_initialized(); + return _region_mark_stats[region].live_words() * HeapWordSize; +} + +inline void G1ConcurrentMark::set_live_bytes(uint region, size_t live_bytes) { + assert_fully_initialized(); + _region_mark_stats[region]._live_words.store_relaxed(live_bytes / HeapWordSize); +} + +inline size_t G1ConcurrentMark::incoming_refs(uint region) const { + assert_fully_initialized(); + return _region_mark_stats[region].incoming_refs(); +} + inline void G1CMTask::abort_marking_if_regular_check_fail() { if (!regular_clock_call()) { set_has_aborted(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp index 3eda7200e25..02c1d2bf0d3 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp @@ -65,7 +65,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public _freed_bytes += hr->used(); hr->set_containing_set(nullptr); hr->clear_both_card_tables(); - _cm->clear_statistics(hr); + _cm->assert_statistics_clear(hr); G1HeapRegionPrinter::mark_reclaim(hr); _g1h->concurrent_refine()->notify_region_reclaimed(hr); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 8fc4d7a2e20..948897a538c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -77,7 +77,7 @@ double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* policy, bool remark) { void G1ConcurrentMarkThread::delay_to_keep_mmu(bool remark) { G1Policy* policy = G1CollectedHeap::heap()->policy(); - if (policy->use_adaptive_young_list_length()) { + if (policy->use_adaptive_num_young_regions()) { double delay_end_sec = mmu_delay_end(policy, remark); // Wait for timeout or thread termination request. MonitorLocker ml(G1CGC_lock, Monitor::_no_safepoint_check_flag); @@ -120,10 +120,10 @@ void G1ConcurrentMarkThread::run_service() { concurrent_cycle_start(); - if (_state == FullCycleMarking) { + if (state() == FullCycleMarking) { concurrent_mark_cycle_do(); } else { - assert(_state == UndoCycleResetForNextCycle, "Must do undo mark but is %d", _state); + assert(state() == UndoCycleResetForNextCycle, "Must do undo mark but is %d", state()); concurrent_undo_cycle_do(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index e75298fdcb4..a1c684ecf59 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1CONCURRENTMARKTHREAD_HPP #include "gc/shared/concurrentGCThread.hpp" +#include "runtime/atomic.hpp" class G1ConcurrentMark; class G1Policy; @@ -47,7 +48,10 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { UndoCycleResetForNextCycle }; - volatile ServiceState _state; + Atomic _state; + + ServiceState state() const { return _state.load_acquire(); } + void set_state(ServiceState new_state) { _state.release_store(new_state); } // Returns whether we are in a "Full" cycle. bool is_in_full_concurrent_cycle() const; @@ -112,7 +116,7 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { bool is_in_progress() const; bool is_in_marking() const; - bool is_in_rebuild_or_scrub() const; + bool is_in_marking_or_rebuild() const; bool is_in_reset_for_next_cycle() const; bool is_in_undo_cycle() const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index 64441ccac65..3225c253dbb 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -41,55 +41,56 @@ inline double G1ConcurrentMarkThread::worker_threads_cpu_time_s() { } inline bool G1ConcurrentMarkThread::is_in_full_concurrent_cycle() const { - ServiceState state = _state; - return (state == FullCycleMarking || state == FullCycleRebuildOrScrub || state == FullCycleResetForNextCycle); + ServiceState st = state(); + return (st == FullCycleMarking || st == FullCycleRebuildOrScrub || st == FullCycleResetForNextCycle); } inline void G1ConcurrentMarkThread::set_idle() { // Concurrent cycle may be aborted any time. assert(!is_idle(), "must not be idle"); - _state = Idle; + set_state(Idle); } inline void G1ConcurrentMarkThread::start_full_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(is_idle(), "cycle in progress"); - _state = FullCycleMarking; + set_state(FullCycleMarking); } inline void G1ConcurrentMarkThread::start_undo_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(is_idle(), "cycle in progress"); - _state = UndoCycleResetForNextCycle; + set_state(UndoCycleResetForNextCycle); } inline void G1ConcurrentMarkThread::set_full_cycle_rebuild_and_scrub() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); - assert(_state == FullCycleMarking, "must be"); - _state = FullCycleRebuildOrScrub; + assert(state() == FullCycleMarking, "must be"); + set_state(FullCycleRebuildOrScrub); } inline void G1ConcurrentMarkThread::set_full_cycle_reset_for_next_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); - assert(_state == FullCycleRebuildOrScrub, "must be"); - _state = FullCycleResetForNextCycle; + assert(state() == FullCycleRebuildOrScrub, "must be"); + set_state(FullCycleResetForNextCycle); } inline bool G1ConcurrentMarkThread::is_in_marking() const { - return _state == FullCycleMarking; + return state() == FullCycleMarking; } -inline bool G1ConcurrentMarkThread::is_in_rebuild_or_scrub() const { - return _state == FullCycleRebuildOrScrub; +inline bool G1ConcurrentMarkThread::is_in_marking_or_rebuild() const { + ServiceState st = state(); + return st == FullCycleMarking || st == FullCycleRebuildOrScrub; } inline bool G1ConcurrentMarkThread::is_in_reset_for_next_cycle() const { - ServiceState state = _state; - return state == FullCycleResetForNextCycle || state == UndoCycleResetForNextCycle; + ServiceState st = state(); + return st == FullCycleResetForNextCycle || st == UndoCycleResetForNextCycle; } inline bool G1ConcurrentMarkThread::is_idle() const { - return _state == Idle; + return state() == Idle; } inline bool G1ConcurrentMarkThread::is_in_progress() const { @@ -97,7 +98,7 @@ inline bool G1ConcurrentMarkThread::is_in_progress() const { } inline bool G1ConcurrentMarkThread::is_in_undo_cycle() const { - return _state == UndoCycleResetForNextCycle; + return state() == UndoCycleResetForNextCycle; } #endif // SHARE_GC_G1_G1CONCURRENTMARKTHREAD_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp index cd560a41333..5b652f096a7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp @@ -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 @@ -180,7 +180,10 @@ class G1RebuildRSAndScrubTask : public WorkerTask { assert(should_rebuild_or_scrub(hr), "must be"); log_trace(gc, marking)("Scrub and rebuild region: " HR_FORMAT " pb: " PTR_FORMAT " TARS: " PTR_FORMAT " TAMS: " PTR_FORMAT, - HR_FORMAT_PARAMS(hr), p2i(pb), p2i(_cm->top_at_rebuild_start(hr)), p2i(_cm->top_at_mark_start(hr))); + HR_FORMAT_PARAMS(hr), + p2i(pb), + p2i(_cm->top_at_rebuild_start(hr)), + p2i(_cm->top_at_mark_start_or_bottom(hr))); { // Step 1: Scan the given region from bottom to parsable_bottom. diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index d58d980b651..c1c820cb554 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -381,7 +381,7 @@ void G1ConcurrentRefineSweepState::complete_refinement(jlong total_yield_during_ policy->record_refinement_stats(stats()); { - MutexLocker x(G1ReviseYoungLength_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1ReviseNumYoungRegions_lock, Mutex::_no_safepoint_check_flag); policy->record_dirtying_stats(TimeHelper::counter_to_millis(g1h->last_refinement_epoch_start()), TimeHelper::counter_to_millis(next_epoch_start), _stats.cards_pending(), @@ -575,7 +575,7 @@ bool G1ConcurrentRefine::adjust_num_threads_periodically() { if (!_needs_adjust) { Tickspan since_adjust = Ticks::now() - _last_adjust; if (since_adjust.milliseconds() < adjust_threads_period_ms()) { - _num_threads_wanted = 0; + _num_threads_wanted.store_relaxed(0); return false; } } @@ -592,7 +592,7 @@ bool G1ConcurrentRefine::adjust_num_threads_periodically() { _needs_adjust = true; } - return (_num_threads_wanted > 0) && !heap_was_locked(); + return (num_threads_wanted() > 0) && !heap_was_locked(); } void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { @@ -603,7 +603,7 @@ void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { size_t num_cards = policy->current_pending_cards(); - _threads_needed.update(_num_threads_wanted, + _threads_needed.update(num_threads_wanted(), available_bytes, num_cards, _pending_cards_target); @@ -613,7 +613,7 @@ void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { new_wanted = _thread_control.max_num_threads(); } - _num_threads_wanted = new_wanted; + _num_threads_wanted.store_relaxed(new_wanted); log_debug(gc, refine)("Concurrent refinement: wanted %u, pending cards: %zu (pending-from-gc %zu), " "predicted: %zu, goal %zu, time-until-next-gc: %1.2fms pred-refine-rate %1.2fc/ms log-rate %1.2fc/ms", diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp index 50fb412f3af..62e56c14c68 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/g1/g1ConcurrentRefineThreadsNeeded.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" @@ -212,7 +213,7 @@ public: // class G1ConcurrentRefine : public CHeapObj { G1Policy* _policy; - volatile uint _num_threads_wanted; + Atomic _num_threads_wanted; size_t _pending_cards_target; Ticks _last_adjust; Ticks _last_deactivate; @@ -306,7 +307,7 @@ public: // obtaining the heap lock. bool heap_was_locked() const { return _heap_was_locked; } - uint num_threads_wanted() const { return _num_threads_wanted; } + uint num_threads_wanted() const { return _num_threads_wanted.load_relaxed(); } uint max_num_threads() const { return _thread_control.max_num_threads(); } // Iterate over all concurrent refinement threads applying the given closure. diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp index 2f99611bb99..b0cf8353dfb 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp @@ -180,11 +180,11 @@ void G1ConcurrentRefineSweepTask::work(uint worker_id) { _scan_state->heap_region_iterate_from_worker_offset(&sweep_cl, worker_id, _max_workers); if (!sweep_cl._completed) { - _sweep_completed = false; + _sweep_completed.store_relaxed(false); } _stats->inc_sweep_duration(os::elapsed_counter() - start); _stats->add_atomic(&sweep_cl._per_worker_refine_data); } -bool G1ConcurrentRefineSweepTask::sweep_completed() const { return _sweep_completed; } +bool G1ConcurrentRefineSweepTask::sweep_completed() const { return _sweep_completed.load_relaxed(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp index 827b9a3c402..f1180f1bb8d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.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 @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1CONCURRENTREFINESWEEPTASK_HPP #include "gc/shared/workerThread.hpp" +#include "runtime/atomic.hpp" class G1CardTableClaimTable; class G1ConcurrentRefineStats; @@ -34,7 +35,7 @@ class G1ConcurrentRefineSweepTask : public WorkerTask { G1CardTableClaimTable* _scan_state; G1ConcurrentRefineStats* _stats; uint _max_workers; - bool _sweep_completed; + Atomic _sweep_completed; public: diff --git a/src/hotspot/share/gc/g1/g1EdenRegions.hpp b/src/hotspot/share/gc/g1/g1EdenRegions.hpp index 7cb4f93519e..14a2eb65329 100644 --- a/src/hotspot/share/gc/g1/g1EdenRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EdenRegions.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 @@ -27,15 +27,15 @@ #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1RegionsOnNodes.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/debug.hpp" class G1EdenRegions { -private: - uint _length; + uint _length; // Sum of used bytes from all retired eden regions. // I.e. updated when mutator regions are retired. - volatile size_t _used_bytes; + Atomic _used_bytes; G1RegionsOnNodes _regions_on_node; public: @@ -49,17 +49,17 @@ public: void clear() { _length = 0; - _used_bytes = 0; + _used_bytes.store_relaxed(0); _regions_on_node.clear(); } uint length() const { return _length; } uint regions_on_node(uint node_index) const { return _regions_on_node.count(node_index); } - size_t used_bytes() const { return _used_bytes; } + size_t used_bytes() const { return _used_bytes.load_relaxed(); } void add_used_bytes(size_t used_bytes) { - _used_bytes += used_bytes; + _used_bytes.add_then_fetch(used_bytes, memory_order_relaxed); } }; diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index cf153226920..1e838b344b7 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -23,6 +23,7 @@ */ #include "classfile/classLoaderDataGraph.hpp" +#include "code/codeCache.hpp" #include "cppstdlib/new.hpp" #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1FullCollector.inline.hpp" @@ -192,7 +193,7 @@ void G1FullCollector::prepare_collection() { // Verification needs the bitmap, so we should clear the bitmap only later. bool in_concurrent_cycle = _heap->abort_concurrent_cycle(); - _heap->verify_before_full_collection(); + _heap->verify_before_full_collection(in_concurrent_cycle); if (in_concurrent_cycle) { GCTraceTime(Debug, gc) debug("Clear Bitmap"); _heap->concurrent_mark()->clear_bitmap(_heap->workers()); @@ -330,6 +331,8 @@ void G1FullCollector::phase1_mark_live_objects() { assert(marker(0)->task_queue()->is_empty(), "Should be no oops on the stack"); } + CodeCache::on_gc_marking_cycle_finish(); + { GCTraceTime(Debug, gc, phases) debug("Phase 1: Flush Mark Stats Cache", scope()->timer()); for (uint i = 0; i < workers(); i++) { diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp index 86297086b44..031f8ecf601 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp @@ -60,13 +60,13 @@ G1FullGCPrepareTask::G1FullGCPrepareTask(G1FullCollector* collector) : } void G1FullGCPrepareTask::set_has_free_compaction_targets() { - if (!_has_free_compaction_targets) { - _has_free_compaction_targets = true; + if (!has_free_compaction_targets()) { + _has_free_compaction_targets.store_relaxed(true); } } bool G1FullGCPrepareTask::has_free_compaction_targets() { - return _has_free_compaction_targets; + return _has_free_compaction_targets.load_relaxed(); } void G1FullGCPrepareTask::work(uint worker_id) { diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp index 9d2887c01a6..57bb036d6a0 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1FullGCTask.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class G1CollectedHeap; class G1CMBitMap; @@ -61,7 +62,7 @@ public: }; class G1FullGCPrepareTask : public G1FullGCTask { - volatile bool _has_free_compaction_targets; + Atomic _has_free_compaction_targets; G1HeapRegionClaimer _hrclaimer; void set_has_free_compaction_targets(); diff --git a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp index c8fba3459aa..310cc4297c6 100644 --- a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp @@ -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 @@ -35,6 +35,7 @@ void G1FullGCResetMetadataTask::G1ResetMetadataClosure::reset_region_metadata(G1 "Non-humongous regions must not have cset group"); hr->rem_set()->clear(); hr->clear_both_card_tables(); + _g1h->concurrent_mark()->reset_region_marking_state(hr); } bool G1FullGCResetMetadataTask::G1ResetMetadataClosure::do_heap_region(G1HeapRegion* hr) { diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index a04b50ec1e7..e5bf8137811 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -94,7 +94,8 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[GCWorkerTotal] = new WorkerDataArray("GCWorkerTotal", "GC Worker Total (ms):", max_gc_threads); _gc_par_phases[GCWorkerEnd] = new WorkerDataArray("GCWorkerEnd", "GC Worker End (ms):", max_gc_threads); _gc_par_phases[Other] = new WorkerDataArray("Other", "GC Worker Other (ms):", max_gc_threads); - _gc_par_phases[MergePSS] = new WorkerDataArray("MergePSS", "Merge Per-Thread State (ms):", max_gc_threads); + _gc_par_phases[FlushPSS] = new WorkerDataArray("FlushPSS", "Flush Per-Thread State (ms):", max_gc_threads); + _gc_par_phases[DestroyPSS] = new WorkerDataArray("DestroyPSS", "Destroy Per-Thread State (ms):", max_gc_threads); _gc_par_phases[RestoreEvacuationFailedRegions] = new WorkerDataArray("RestoreEvacuationFailedRegions", "Restore Evacuation Failed Regions (ms):", max_gc_threads); _gc_par_phases[RemoveSelfForwards] = new WorkerDataArray("RemoveSelfForwards", "Remove Self Forwards (ms):", max_gc_threads); _gc_par_phases[ClearCardTable] = new WorkerDataArray("ClearPendingCards", "Clear Pending Cards (ms):", max_gc_threads); @@ -103,7 +104,7 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[UpdateDerivedPointers] = new WorkerDataArray("UpdateDerivedPointers", "Update Derived Pointers (ms):", max_gc_threads); #endif // COMPILER2 _gc_par_phases[EagerlyReclaimHumongousObjects] = new WorkerDataArray("EagerlyReclaimHumongousObjects", "Eagerly Reclaim Humongous Objects (ms):", max_gc_threads); - _gc_par_phases[ResetPartialArrayStateManager] = new WorkerDataArray("ResetPartialArrayStateManager", "Reset Partial Array State Manager (ms):", max_gc_threads); + _gc_par_phases[UpdateCodeRoots] = new WorkerDataArray("UpdateCodeRoots", "Update Code Roots (ms):", _max_gc_threads); _gc_par_phases[ProcessEvacuationFailedRegions] = new WorkerDataArray("ProcessEvacuationFailedRegions", "Process Evacuation Failed Regions (ms):", max_gc_threads); _gc_par_phases[ScanHR]->create_thread_work_items("Pending Cards:", ScanHRPendingCards); @@ -126,13 +127,13 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[OptCodeRoots]->create_thread_work_items("Scanned Nmethods:", CodeRootsScannedNMethods); - _gc_par_phases[MergePSS]->create_thread_work_items("Copied Bytes:", MergePSSCopiedBytes); - _gc_par_phases[MergePSS]->create_thread_work_items("LAB Waste:", MergePSSLABWasteBytes); - _gc_par_phases[MergePSS]->create_thread_work_items("LAB Undo Waste:", MergePSSLABUndoWasteBytes); - _gc_par_phases[MergePSS]->create_thread_work_items("Pending Cards:", MergePSSPendingCards); - _gc_par_phases[MergePSS]->create_thread_work_items("To-Young-Gen Cards:", MergePSSToYoungGenCards); - _gc_par_phases[MergePSS]->create_thread_work_items("Evac-Fail Cards:", MergePSSEvacFail); - _gc_par_phases[MergePSS]->create_thread_work_items("Marked Cards:", MergePSSMarked); + _gc_par_phases[FlushPSS]->create_thread_work_items("Copied Bytes:", FlushPSSCopiedBytes); + _gc_par_phases[FlushPSS]->create_thread_work_items("LAB Waste:", FlushPSSLABWasteBytes); + _gc_par_phases[FlushPSS]->create_thread_work_items("LAB Undo Waste:", FlushPSSLABUndoWasteBytes); + _gc_par_phases[FlushPSS]->create_thread_work_items("Pending Cards:", FlushPSSPendingCards); + _gc_par_phases[FlushPSS]->create_thread_work_items("To-Young-Gen Cards:", FlushPSSToYoungGenCards); + _gc_par_phases[FlushPSS]->create_thread_work_items("Evac-Fail Cards:", FlushPSSEvacFail); + _gc_par_phases[FlushPSS]->create_thread_work_items("Marked Cards:", FlushPSSMarked); _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Evacuation Failed Regions:", RestoreEvacFailureRegionsEvacFailedNum); _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Pinned Regions:", RestoreEvacFailureRegionsPinnedNum); @@ -495,7 +496,8 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set(bool evacuation_failed _weak_phase_times.log_subtotals(3); debug_time("Post Evacuate Cleanup 1", _cur_post_evacuate_cleanup_1_time_ms); - debug_phase(_gc_par_phases[MergePSS], 1); + debug_phase(_gc_par_phases[FlushPSS], 1); + debug_phase(_gc_par_phases[UpdateCodeRoots], 1); debug_phase(_gc_par_phases[ClearCardTable], 1); debug_phase(_gc_par_phases[RecalculateUsed], 1); if (evacuation_failed) { @@ -512,7 +514,7 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set(bool evacuation_failed debug_phase(_gc_par_phases[UpdateDerivedPointers], 1); #endif // COMPILER2 debug_phase(_gc_par_phases[EagerlyReclaimHumongousObjects], 1); - trace_phase(_gc_par_phases[ResetPartialArrayStateManager]); + trace_phase(_gc_par_phases[DestroyPSS]); if (G1CollectedHeap::heap()->should_sample_collection_set_candidates()) { debug_phase(_gc_par_phases[SampleCollectionSetCandidates], 1); diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp index 31bfd38ddb9..078a819986c 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp @@ -76,7 +76,7 @@ class G1GCPhaseTimes : public CHeapObj { ResizeThreadLABs, RebuildFreeList, SampleCollectionSetCandidates, - MergePSS, + FlushPSS, RestoreEvacuationFailedRegions, RemoveSelfForwards, ClearCardTable, @@ -85,7 +85,8 @@ class G1GCPhaseTimes : public CHeapObj { UpdateDerivedPointers, #endif // COMPILER2 EagerlyReclaimHumongousObjects, - ResetPartialArrayStateManager, + UpdateCodeRoots, + DestroyPSS, ProcessEvacuationFailedRegions, ResetMarkingState, NoteStartOfMark, @@ -134,15 +135,15 @@ class G1GCPhaseTimes : public CHeapObj { CodeRootsScannedNMethods }; - enum GCMergePSSWorkItems { - MergePSSCopiedBytes, - MergePSSLABSize, - MergePSSLABWasteBytes, - MergePSSLABUndoWasteBytes, - MergePSSPendingCards, // To be scanned cards generated by GC (from cross-references and evacuation failure). - MergePSSToYoungGenCards, // To-young-gen cards generated by GC. - MergePSSEvacFail, // Evacuation failure generated dirty cards by GC. - MergePSSMarked, // Total newly marked cards. + enum GCFlushPSSWorkItems { + FlushPSSCopiedBytes, + FlushPSSLABSize, + FlushPSSLABWasteBytes, + FlushPSSLABUndoWasteBytes, + FlushPSSPendingCards, // To be scanned cards generated by GC (from cross-references and evacuation failure). + FlushPSSToYoungGenCards, // To-young-gen cards generated by GC. + FlushPSSEvacFail, // Evacuation failure generated dirty cards by GC. + FlushPSSMarked, // Total newly marked cards. }; enum RestoreEvacFailureRegionsWorkItems { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 2052a3ce156..2c85e2fcc0d 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -129,8 +129,6 @@ void G1HeapRegion::hr_clear(bool clear_space) { rem_set()->clear(); - G1CollectedHeap::heap()->concurrent_mark()->reset_top_at_mark_start(this); - _parsable_bottom.store_relaxed(bottom()); _garbage_bytes.store_relaxed(0); _incoming_refs = 0; @@ -415,6 +413,8 @@ bool G1HeapRegion::verify_code_roots(VerifyOption vo) const { return has_code_roots; } + rem_set()->reset_code_root_table_scanner(); + VerifyCodeRootNMethodClosure nm_cl(this); code_roots_do(&nm_cl); @@ -439,7 +439,9 @@ void G1HeapRegion::print_on(outputStream* st) const { } G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); st->print("|TAMS " PTR_FORMAT "| PB " PTR_FORMAT "| %-9s ", - p2i(cm->top_at_mark_start(this)), p2i(parsable_bottom_acquire()), rem_set()->get_state_str()); + p2i(cm->top_at_mark_start_or_bottom(this)), + p2i(parsable_bottom_acquire()), + rem_set()->get_state_str()); if (UseNUMA) { G1NUMA* numa = G1NUMA::numa(); if (node_index() < numa->num_active_nodes()) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp index 619aef35a9a..87597d450cb 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp @@ -152,10 +152,6 @@ inline void G1HeapRegion::reset_skip_compacting_after_full_gc() { } inline void G1HeapRegion::reset_after_full_gc_common() { - // After a full gc the mark information in a movable region is invalid. Reset marking - // information. - G1CollectedHeap::heap()->concurrent_mark()->reset_top_at_mark_start(this); - // Everything above bottom() is parsable and live. reset_parsable_bottom(); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp index eb593ff408e..e24610c55d8 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp @@ -71,8 +71,8 @@ class G1HeapRegionTable : public G1BiasedMappedArray { // class G1HeapRegionManager: public CHeapObj { - friend class VMStructs; friend class G1HeapRegionClaimer; + friend class VMStructs; G1RegionToSpaceMapper* _bot_mapper; G1RegionToSpaceMapper* _card_table_mapper; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index 13c7a6a8d3e..e2009b0e77d 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -87,8 +87,12 @@ void G1HeapRegionRemSet::clear(bool only_cardset, bool keep_tracked) { } } -void G1HeapRegionRemSet::reset_table_scanner() { +void G1HeapRegionRemSet::reset_code_root_table_scanner() { _code_roots.reset_table_scanner(); +} + +void G1HeapRegionRemSet::reset_table_scanner() { + reset_code_root_table_scanner(); if (has_cset_group()) { card_set()->reset_table_scanner(); } @@ -128,6 +132,10 @@ void G1HeapRegionRemSet::bulk_remove_code_roots() { _code_roots.bulk_remove(); } +void G1HeapRegionRemSet::prepare_for_adding_code_roots(size_t num_code_roots) { + _code_roots.prepare_for_adding_code_roots(num_code_roots); +} + void G1HeapRegionRemSet::code_roots_do(NMethodClosure* blk) const { _code_roots.nmethods_do(blk); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 950098c706e..20f7b785f45 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -154,6 +154,7 @@ public: // entries for this region in other remsets. void clear(bool only_cardset = false, bool keep_tracked = false); + void reset_code_root_table_scanner(); void reset_table_scanner(); G1MonotonicArenaMemoryStats card_set_memory_stats() const; @@ -181,6 +182,7 @@ public: void add_code_root(nmethod* nm); void remove_code_root(nmethod* nm); void bulk_remove_code_roots(); + void prepare_for_adding_code_roots(size_t num_code_roots); // Applies blk->do_nmethod() to each of the entries in _code_roots void code_roots_do(NMethodClosure* blk) const; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionType.cpp b/src/hotspot/share/gc/g1/g1HeapRegionType.cpp index ba6bf7e870d..c62bac8bcbc 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionType.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionType.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 @@ -45,8 +45,7 @@ bool G1HeapRegionType::is_valid(Tag tag) { } const char* G1HeapRegionType::get_str() const { - hrt_assert_is_valid(_tag); - switch (_tag) { + switch (get()) { case FreeTag: return "FREE"; case EdenTag: return "EDEN"; case SurvTag: return "SURV"; @@ -60,8 +59,7 @@ const char* G1HeapRegionType::get_str() const { } const char* G1HeapRegionType::get_short_str() const { - hrt_assert_is_valid(_tag); - switch (_tag) { + switch (get()) { case FreeTag: return "F"; case EdenTag: return "E"; case SurvTag: return "S"; @@ -75,8 +73,7 @@ const char* G1HeapRegionType::get_short_str() const { } G1HeapRegionTraceType::Type G1HeapRegionType::get_trace_type() { - hrt_assert_is_valid(_tag); - switch (_tag) { + switch (get()) { case FreeTag: return G1HeapRegionTraceType::Free; case EdenTag: return G1HeapRegionTraceType::Eden; case SurvTag: return G1HeapRegionTraceType::Survivor; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionType.hpp b/src/hotspot/share/gc/g1/g1HeapRegionType.hpp index 839df68febd..3ffa7faecff 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionType.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionType.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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,15 +26,15 @@ #define SHARE_GC_G1_G1HEAPREGIONTYPE_HPP #include "gc/g1/g1HeapRegionTraceType.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #define hrt_assert_is_valid(tag) \ assert(is_valid((tag)), "invalid HR type: %u", (uint) (tag)) class G1HeapRegionType { -friend class VMStructs; + friend class VMStructs; -private: // We encode the value of the heap region type so the generation can be // determined quickly. The tag is split into two parts: // @@ -73,20 +73,21 @@ private: OldTag = OldMask } Tag; - volatile Tag _tag; + Atomic _tag; static bool is_valid(Tag tag); Tag get() const { - hrt_assert_is_valid(_tag); - return _tag; + Tag result = _tag.load_relaxed(); + hrt_assert_is_valid(result); + return result; } // Sets the type to 'tag'. void set(Tag tag) { hrt_assert_is_valid(tag); - hrt_assert_is_valid(_tag); - _tag = tag; + hrt_assert_is_valid(_tag.load_relaxed()); + _tag.store_relaxed(tag); } // Sets the type to 'tag', expecting the type to be 'before'. This @@ -95,13 +96,12 @@ private: void set_from(Tag tag, Tag before) { hrt_assert_is_valid(tag); hrt_assert_is_valid(before); - hrt_assert_is_valid(_tag); - assert(_tag == before, "HR tag: %u, expected: %u new tag; %u", _tag, before, tag); - _tag = tag; + assert(get() == before, "HR tag: %u, expected: %u new tag; %u", get(), before, tag); + _tag.store_relaxed(tag); } // Private constructor used for static constants - G1HeapRegionType(Tag t) : _tag(t) { hrt_assert_is_valid(_tag); } + G1HeapRegionType(Tag t) : _tag(t) { hrt_assert_is_valid(t); } public: // Queries @@ -159,7 +159,15 @@ public: const char* get_short_str() const; G1HeapRegionTraceType::Type get_trace_type(); - G1HeapRegionType() : _tag(FreeTag) { hrt_assert_is_valid(_tag); } + G1HeapRegionType() : G1HeapRegionType(FreeTag) { } + + G1HeapRegionType(const G1HeapRegionType& other) : G1HeapRegionType(other.get()) { } + G1HeapRegionType& operator=(const G1HeapRegionType& other) { + if (this != &other) { + set(other.get()); + } + return *this; + } static const G1HeapRegionType Eden; static const G1HeapRegionType Survivor; diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp index 1b9704e8ad3..6158bc47c47 100644 --- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp @@ -185,7 +185,7 @@ size_t G1HeapSizingPolicy::young_collection_shrink_amount(double cpu_usage_delta // going to use during this mutator phase. uint target_regions_to_shrink = _g1h->num_free_regions(); - uint needed_for_allocation = _g1h->eden_target_length(); + uint needed_for_allocation = _g1h->target_num_eden_regions(); if (_g1h->is_humongous(allocation_word_size)) { needed_for_allocation += (uint) _g1h->humongous_obj_size_in_regions(allocation_word_size); } diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.cpp b/src/hotspot/share/gc/g1/g1HeapTransition.cpp index 690bda4e7e6..e91afc79267 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.cpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,13 +29,13 @@ #include "memory/metaspaceUtils.hpp" G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : - _eden_length(g1_heap->eden_regions_count()), - _survivor_length(g1_heap->survivor_regions_count()), - _old_length(g1_heap->old_regions_count()), - _humongous_length(g1_heap->humongous_regions_count()), + _num_eden_regions(g1_heap->eden_regions_count()), + _num_survivor_regions(g1_heap->survivor_regions_count()), + _num_old_regions(g1_heap->old_regions_count()), + _num_humongous_regions(g1_heap->humongous_regions_count()), _meta_sizes(MetaspaceUtils::get_combined_statistics()), - _eden_length_per_node(nullptr), - _survivor_length_per_node(nullptr) { + _num_eden_regions_per_node(nullptr), + _num_survivor_regions_per_node(nullptr) { uint node_count = G1NUMA::numa()->num_active_nodes(); @@ -43,20 +43,20 @@ G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : LogTarget(Debug, gc, heap, numa) lt; if (lt.is_enabled()) { - _eden_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); - _survivor_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); + _num_eden_regions_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); + _num_survivor_regions_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); for (uint i = 0; i < node_count; i++) { - _eden_length_per_node[i] = g1_heap->eden_regions_count(i); - _survivor_length_per_node[i] = g1_heap->survivor_regions_count(i); + _num_eden_regions_per_node[i] = g1_heap->eden_regions_count(i); + _num_survivor_regions_per_node[i] = g1_heap->survivor_regions_count(i); } } } } G1HeapTransition::Data::~Data() { - FREE_C_HEAP_ARRAY(_eden_length_per_node); - FREE_C_HEAP_ARRAY(_survivor_length_per_node); + FREE_C_HEAP_ARRAY(_num_eden_regions_per_node); + FREE_C_HEAP_ARRAY(_num_survivor_regions_per_node); } G1HeapTransition::G1HeapTransition(G1CollectedHeap* g1_heap) : _g1_heap(g1_heap), _before(g1_heap) { } @@ -101,23 +101,23 @@ public: } }; -static void log_regions(const char* msg, size_t before_length, size_t after_length, size_t capacity, - uint* before_per_node_length, uint* after_per_node_length) { +static void log_regions(const char* msg, size_t num_before, size_t num_after, size_t capacity, + uint* num_per_node_before, uint* num_per_node_after) { LogTarget(Info, gc, heap) lt; if (lt.is_enabled()) { LogStream ls(lt); ls.print("%s regions: %zu->%zu(%zu)", - msg, before_length, after_length, capacity); + msg, num_before, num_after, capacity); // Not null only if gc+heap+numa at Debug level is enabled. - if (before_per_node_length != nullptr && after_per_node_length != nullptr) { + if (num_per_node_before != nullptr && num_per_node_after != nullptr) { G1NUMA* numa = G1NUMA::numa(); uint num_nodes = numa->num_active_nodes(); const uint* node_ids = numa->node_ids(); ls.print(" ("); for (uint i = 0; i < num_nodes; i++) { - ls.print("%u: %u->%u", node_ids[i], before_per_node_length[i], after_per_node_length[i]); + ls.print("%u: %u->%u", node_ids[i], num_per_node_before[i], num_per_node_after[i]); // Skip adding below if it is the last one. if (i != num_nodes - 1) { ls.print(", "); @@ -132,8 +132,8 @@ static void log_regions(const char* msg, size_t before_length, size_t after_leng void G1HeapTransition::print() { Data after(_g1_heap); - size_t eden_capacity_length_after_gc = _g1_heap->policy()->young_list_target_length() - after._survivor_length; - size_t survivor_capacity_length_before_gc = _g1_heap->policy()->max_survivor_regions(); + size_t num_eden_after_gc = _g1_heap->policy()->target_num_young_regions() - after._num_survivor_regions; + size_t num_survivor_before_gc = _g1_heap->policy()->max_survivor_regions(); DetailedUsage usage; if (log_is_enabled(Trace, gc, heap)) { @@ -141,32 +141,35 @@ void G1HeapTransition::print() { _g1_heap->heap_region_iterate(&blk); usage = blk._usage; assert(usage._eden_region_count == 0, "Expected no eden regions, but got %zu", usage._eden_region_count); - assert(usage._survivor_region_count == after._survivor_length, "Expected survivors to be %zu but was %zu", - after._survivor_length, usage._survivor_region_count); - assert(usage._old_region_count == after._old_length, "Expected old to be %zu but was %zu", - after._old_length, usage._old_region_count); - assert(usage._humongous_region_count == after._humongous_length, "Expected humongous to be %zu but was %zu", - after._humongous_length, usage._humongous_region_count); + assert(usage._survivor_region_count == after._num_survivor_regions, "Expected survivors to be %zu but was %zu", + after._num_survivor_regions, usage._survivor_region_count); + assert(usage._old_region_count == after._num_old_regions, "Expected old to be %zu but was %zu", + after._num_old_regions, usage._old_region_count); + assert(usage._humongous_region_count == after._num_humongous_regions, "Expected humongous to be %zu but was %zu", + after._num_humongous_regions, usage._humongous_region_count); } - log_regions("Eden", _before._eden_length, after._eden_length, eden_capacity_length_after_gc, - _before._eden_length_per_node, after._eden_length_per_node); + log_regions("Eden", _before._num_eden_regions, after._num_eden_regions, num_eden_after_gc, + _before._num_eden_regions_per_node, after._num_eden_regions_per_node); log_trace(gc, heap)(" Used: 0K, Waste: 0K"); - log_regions("Survivor", _before._survivor_length, after._survivor_length, survivor_capacity_length_before_gc, - _before._survivor_length_per_node, after._survivor_length_per_node); + log_regions("Survivor", _before._num_survivor_regions, after._num_survivor_regions, num_survivor_before_gc, + _before._num_survivor_regions_per_node, after._num_survivor_regions_per_node); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._survivor_used / K, ((after._survivor_length * G1HeapRegion::GrainBytes) - usage._survivor_used) / K); + usage._survivor_used / K, + ((after._num_survivor_regions * G1HeapRegion::GrainBytes) - usage._survivor_used) / K); log_info(gc, heap)("Old regions: %zu->%zu", - _before._old_length, after._old_length); + _before._num_old_regions, after._num_old_regions); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._old_used / K, ((after._old_length * G1HeapRegion::GrainBytes) - usage._old_used) / K); + usage._old_used / K, + ((after._num_old_regions * G1HeapRegion::GrainBytes) - usage._old_used) / K); log_info(gc, heap)("Humongous regions: %zu->%zu", - _before._humongous_length, after._humongous_length); + _before._num_humongous_regions, after._num_humongous_regions); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._humongous_used / K, ((after._humongous_length * G1HeapRegion::GrainBytes) - usage._humongous_used) / K); + usage._humongous_used / K, + ((after._num_humongous_regions * G1HeapRegion::GrainBytes) - usage._humongous_used) / K); MetaspaceUtils::print_metaspace_change(_before._meta_sizes); } diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.hpp b/src/hotspot/share/gc/g1/g1HeapTransition.hpp index 18bcd153505..4b69d26c5a6 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.hpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.hpp @@ -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 @@ -35,16 +35,16 @@ class G1HeapTransition { class DetailedUsageClosure; struct Data { - size_t _eden_length; - size_t _survivor_length; - size_t _old_length; - size_t _humongous_length; + size_t _num_eden_regions; + size_t _num_survivor_regions; + size_t _num_old_regions; + size_t _num_humongous_regions; const MetaspaceCombinedStats _meta_sizes; // Only includes current eden regions. - uint* _eden_length_per_node; + uint* _num_eden_regions_per_node; // Only includes current survivor regions. - uint* _survivor_length_per_node; + uint* _num_survivor_regions_per_node; Data(G1CollectedHeap* g1_heap); ~Data(); diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 304722c13a1..7b518379a73 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -499,12 +499,14 @@ void G1HeapVerifier::verify_marking_state() { // Verify TAMSes, bitmaps and liveness statistics. // // - if part of marking: TAMS != bottom, liveness == 0, bitmap clear - // - if evacuation failed + part of marking: TAMS != bottom, liveness != 0, bitmap has at least on object set (corresponding to liveness) + // - if evacuation failed + part of marking: TAMS != bottom, liveness != 0, bitmap has at least one + // object set (corresponding to liveness) // - if not part of marking: TAMS == bottom, liveness == 0, bitmap clear; must be in root region // To compare liveness recorded in G1ConcurrentMark and actual we need to flush the - // cache. - G1CollectedHeap::heap()->concurrent_mark()->flush_all_task_caches(); + // cache. Do not signal end of use of the mark stats cache as this flush is only to + // make verification work. Further concurrent marking continues to need these values. + G1CollectedHeap::heap()->concurrent_mark()->flush_all_task_caches(false /* ends_use_of_mark_cache */); G1VerifyRegionMarkingStateClosure cl; _g1h->heap_region_iterate(&cl); @@ -532,28 +534,32 @@ void G1HeapVerifier::verify_after_gc() { verify_card_tables_in_sync(); } -void G1HeapVerifier::verify_bitmap_clear(bool from_tams) { +void G1HeapVerifier::verify_bitmap_clear(bool from_tams, bool concurrent_cycle_aborted) { if (!G1VerifyBitmaps) { return; } class G1VerifyBitmapClear : public G1HeapRegionClosure { bool _from_tams; + bool _concurrent_cycle_aborted; public: - G1VerifyBitmapClear(bool from_tams) : _from_tams(from_tams) { } + G1VerifyBitmapClear(bool from_tams, bool concurrent_cycle_aborted) : + _from_tams(from_tams), _concurrent_cycle_aborted(concurrent_cycle_aborted) { } virtual bool do_heap_region(G1HeapRegion* r) { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); G1CMBitMap* bitmap = cm->mark_bitmap(); - HeapWord* start = _from_tams ? cm->top_at_mark_start(r) : r->bottom(); + HeapWord* start = _from_tams + ? cm->top_at_mark_start_for_verification(r, _concurrent_cycle_aborted) + : r->bottom(); HeapWord* mark = bitmap->get_next_marked_addr(start, r->end()); guarantee(mark == r->end(), "Found mark at " PTR_FORMAT " in region %u from start " PTR_FORMAT, p2i(mark), r->hrm_index(), p2i(start)); return false; } - } cl(from_tams); + } cl(from_tams, concurrent_cycle_aborted); G1CollectedHeap::heap()->heap_region_iterate(&cl); } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp index 55f6646563f..05a89a524d6 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.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 @@ -73,7 +73,8 @@ public: // Verify that marking state is set up correctly after a concurrent start pause. void verify_marking_state(); - void verify_bitmap_clear(bool above_tams_only); + void verify_bitmap_clear(bool above_tams_only, + bool concurrent_cycle_aborted = false); // Do sanity check on the contents of the in-cset fast test table. bool check_region_attr_table() PRODUCT_RETURN_( return true; ); diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index 26364705a9d..0af07282fc7 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.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. * 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 @@ void G1MonitoringSupport::initialize_serviceability() { _full_gc_memory_manager.add_pool(_survivor_space_pool); _full_gc_memory_manager.add_pool(_old_gen_pool); - _conc_gc_memory_manager.add_pool(_old_gen_pool); + _conc_gc_memory_manager.add_pool(_old_gen_pool, false /* always_affected_by_gc */); _young_gc_memory_manager.add_pool(_eden_space_pool); _young_gc_memory_manager.add_pool(_survivor_space_pool); @@ -245,14 +245,14 @@ void G1MonitoringSupport::recalculate_sizes() { // use smaller value to subtract. _old_gen_used = _overall_used - MIN2(_overall_used, _eden_space_used + _survivor_space_used); - uint survivor_list_length = _g1h->survivor_regions_count(); + uint num_survivor_regions = _g1h->survivor_regions_count(); - uint young_list_target_length = _g1h->policy()->young_list_target_length(); - assert(young_list_target_length >= survivor_list_length, "invariant"); - uint eden_list_max_length = young_list_target_length - survivor_list_length; + uint target_num_young_regions = _g1h->policy()->target_num_young_regions(); + assert(target_num_young_regions >= num_survivor_regions, "invariant"); + uint max_num_eden_regions = target_num_young_regions - num_survivor_regions; // First calculate the committed sizes that can be calculated independently. - _survivor_space_committed = survivor_list_length * G1HeapRegion::GrainBytes; + _survivor_space_committed = num_survivor_regions * G1HeapRegion::GrainBytes; _old_gen_committed = G1HeapRegion::align_up_to_region_byte_size(_old_gen_used); // Next, start with the overall committed size. @@ -265,7 +265,7 @@ void G1MonitoringSupport::recalculate_sizes() { committed -= _survivor_space_committed + _old_gen_committed; // Next, calculate and remove the committed size for the eden. - _eden_space_committed = (size_t) eden_list_max_length * G1HeapRegion::GrainBytes; + _eden_space_committed = (size_t) max_num_eden_regions * G1HeapRegion::GrainBytes; // Somewhat defensive: be robust in case there are inaccuracies in // the calculations _eden_space_committed = MIN2(_eden_space_committed, committed); @@ -383,9 +383,10 @@ G1FullGCMonitoringScope::G1FullGCMonitoringScope(G1MonitoringSupport* monitoring "end of major GC") { } -G1ConcGCMonitoringScope::G1ConcGCMonitoringScope(G1MonitoringSupport* monitoring_support) : +G1ConcGCMonitoringScope::G1ConcGCMonitoringScope(G1MonitoringSupport* monitoring_support, bool affects_memory_pools) : G1MonitoringScope(monitoring_support, monitoring_support->_conc_collection_counters, &monitoring_support->_conc_gc_memory_manager, - "end of concurrent GC pause") { + "end of concurrent GC pause", + affects_memory_pools) { } diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp index 31a39f2aca2..8a7040e626d 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.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. * 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,10 +122,10 @@ class MemoryPool; // path as low-overhead as possible. class G1MonitoringSupport : public CHeapObj { - friend class VMStructs; - friend class G1YoungGCMonitoringScope; - friend class G1FullGCMonitoringScope; friend class G1ConcGCMonitoringScope; + friend class G1FullGCMonitoringScope; + friend class G1YoungGCMonitoringScope; + friend class VMStructs; G1CollectedHeap* _g1h; @@ -246,6 +246,6 @@ public: class G1ConcGCMonitoringScope : public G1MonitoringScope { public: - G1ConcGCMonitoringScope(G1MonitoringSupport* monitoring_support); + G1ConcGCMonitoringScope(G1MonitoringSupport* monitoring_support, bool affects_memory_pools); }; #endif // SHARE_GC_G1_G1MONITORINGSUPPORT_HPP diff --git a/src/hotspot/share/gc/g1/g1NMethodClosure.cpp b/src/hotspot/share/gc/g1/g1NMethodClosure.cpp index d74aa5eae1d..d7dcbeb87fb 100644 --- a/src/hotspot/share/gc/g1/g1NMethodClosure.cpp +++ b/src/hotspot/share/gc/g1/g1NMethodClosure.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 @@ -35,13 +35,47 @@ template void G1NMethodClosure::HeapRegionGatheringOopClosure::do_oop_work(T* p) { + T old_oop_or_narrowoop = RawAccess<>::oop_load(p); + _work->do_oop(p); T oop_or_narrowoop = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(oop_or_narrowoop)) { + // If the oop moved, we need to update the code root set at the new location. If it did not + // change, it is either in the existing code root set, or an earlier evacuation round already + // enqueued it for deferred update. + // + // We defer actual update to the code roots to later. This can, in presence of optional + // collections, ultimately result in duplicates in the per-thread code root set update list. + // We consider this negligible, given that optional collection is rare and typically does + // not cover many regions/nmethods. + if (oop_or_narrowoop != old_oop_or_narrowoop) { + // If the oop moved, it must not have been null. + assert(!CompressedOops::is_null(oop_or_narrowoop), "must be"); oop o = CompressedOops::decode_not_null(oop_or_narrowoop); + assert(!_g1h->is_in_cset(o), "must be"); + G1HeapRegion* hr = _g1h->heap_region_containing(o); - assert(!_g1h->is_in_cset(o) || hr->rem_set()->code_roots_list_contains(_nm), "if o still in collection set then evacuation failed and nm must already be in the remset"); - hr->add_code_root(_nm); + _affected_regions.append_if_missing(hr); + } else { + // We could be tempted to verify that for a non-null oop, the _nm is already in the target code root + // set or in one of the deferred code root set update lists. It would not be sufficient to verify the + // current thread's list, because across evacuation rounds (i.e. initial/multiple optional) different + // threads may have worked on a given oop from an nmethod. + // This is rather expensive, not only requiring looking at all threads' lists, but also making sure + // that there are no memory ordering issues when doing that. So we skip it. + } +} + +G1NMethodClosure::HeapRegionGatheringOopClosure::HeapRegionGatheringOopClosure(OopClosure* oc, G1ParScanThreadState* pss) : + _g1h(G1CollectedHeap::heap()), + _work(oc), + _pss(pss), + _nm(nullptr), + _affected_regions(5) { +} + +void G1NMethodClosure::HeapRegionGatheringOopClosure::add_to_remsets() { + while (!_affected_regions.is_empty()) { + _pss->remember_nmethod_into_region(_affected_regions.pop(), _nm); } } @@ -74,11 +108,13 @@ void G1NMethodClosure::MarkingOopClosure::do_oop(narrowOop* o) { } void G1NMethodClosure::do_evacuation_and_fixup(nmethod* nm) { - _oc.set_nm(nm); + _oc.set_nmethod(nm); // Evacuate objects pointed to by the nmethod nm->oops_do(&_oc); + _oc.add_to_remsets(); + if (_strong) { // CodeCache unloading support nm->mark_as_maybe_on_stack(); diff --git a/src/hotspot/share/gc/g1/g1NMethodClosure.hpp b/src/hotspot/share/gc/g1/g1NMethodClosure.hpp index 91906932d4f..95d0ee1942d 100644 --- a/src/hotspot/share/gc/g1/g1NMethodClosure.hpp +++ b/src/hotspot/share/gc/g1/g1NMethodClosure.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 @@ -27,8 +27,10 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "memory/iterator.hpp" +#include "utilities/growableArray.hpp" class G1ConcurrentMark; +class G1ParScanThreadState; class nmethod; class G1NMethodClosure : public NMethodClosure { @@ -36,20 +38,27 @@ class G1NMethodClosure : public NMethodClosure { class HeapRegionGatheringOopClosure : public OopClosure { G1CollectedHeap* _g1h; OopClosure* _work; + G1ParScanThreadState* _pss; + nmethod* _nm; + GrowableArrayCHeap _affected_regions; template void do_oop_work(T* p); public: - HeapRegionGatheringOopClosure(OopClosure* oc) : _g1h(G1CollectedHeap::heap()), _work(oc), _nm(nullptr) {} + HeapRegionGatheringOopClosure(OopClosure* oc, G1ParScanThreadState* pss); + ~HeapRegionGatheringOopClosure() = default; void do_oop(oop* o); void do_oop(narrowOop* o); - void set_nm(nmethod* nm) { + void set_nmethod(nmethod* nm) { + assert(_affected_regions.is_empty(), "must be"); _nm = nm; } + + void add_to_remsets(); }; // Mark all oops below TAMS. @@ -72,8 +81,8 @@ class G1NMethodClosure : public NMethodClosure { bool _strong; public: - G1NMethodClosure(uint worker_id, OopClosure* oc, bool strong) : - _oc(oc), _marking_oc(worker_id), _strong(strong) { } + G1NMethodClosure(uint worker_id, OopClosure* oc, bool strong, G1ParScanThreadState* pss) : + _oc(oc, pss), _marking_oc(worker_id), _strong(strong) { } void do_evacuation_and_fixup(nmethod* nm); void do_marking(nmethod* nm); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 45e1b25cb95..3e6f8758744 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -55,12 +55,20 @@ // Explicit NOINLINE to block ATTRIBUTE_FLATTENing. #define MAYBE_INLINE_EVACUATION NOT_DEBUG(inline) DEBUG_ONLY(NOINLINE) +// Good estimate for the initial table size. +static uint initial_nmethod_table_size(G1CollectedHeap* g1h) { + // The +1 is both to consider the retained old region likely to be added, and avoid zero-sized initial tables. + return MIN3(g1h->collection_set()->num_regions(), g1h->max_num_regions() / 2, g1h->num_available_regions()) + 1; +} + G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, + G1ParScanThreadStateSet* per_thread_states, uint worker_id, uint num_workers, G1CollectionSet* collection_set, G1EvacFailureRegions* evac_failure_regions) : _g1h(g1h), + _per_thread_states(per_thread_states), _task_queue(g1h->task_queue(worker_id)), _ct(g1h->refinement_table()), _closures(nullptr), @@ -76,13 +84,17 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _trim_ticks(), _surviving_young_words_base(nullptr), _surviving_young_words(nullptr), - _surviving_words_length(collection_set->young_region_length() + 1), + _surviving_words_length(collection_set->num_young_regions() + 1), _old_gen_is_full(false), _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()), _obj_alloc_stat(nullptr), + // The initial size estimate is relatively conservative, assuming that all regions + // in the collection set get evacuated into the same amount of new regions. + _nmethods_to_add(initial_nmethod_table_size(g1h), + MAX2(initial_nmethod_table_size(g1h), _g1h->max_num_regions() / 2)), ALLOCATION_FAILURE_INJECTOR_ONLY(_allocation_failure_inject_counter(0) COMMA) _evacuation_failed_info(), _evac_failure_regions(evac_failure_regions), @@ -129,6 +141,12 @@ size_t G1ParScanThreadState::flush_stats(size_t* surviving_young_words, uint num } G1ParScanThreadState::~G1ParScanThreadState() { + auto delete_all = [&] (uint region, G1NmethodSet* nmethods) -> bool { + delete nmethods; + return true; + }; + _nmethods_to_add.iterate(delete_all); + delete _plab_allocator; delete _closures; FREE_C_HEAP_ARRAY(_surviving_young_words_base); @@ -575,6 +593,7 @@ G1ParScanThreadState* G1ParScanThreadStateSet::state_for_worker(uint worker_id) if (_states[worker_id] == nullptr) { _states[worker_id] = new G1ParScanThreadState(_g1h, + this, worker_id, _num_workers, _collection_set, @@ -606,21 +625,59 @@ void G1ParScanThreadStateSet::flush_stats() { size_t evac_failure_cards = pss->num_cards_from_evac_failure(); size_t marked_cards = pss->num_cards_marked(); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, copied_bytes, G1GCPhaseTimes::MergePSSCopiedBytes); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, lab_waste_bytes, G1GCPhaseTimes::MergePSSLABWasteBytes); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, lab_undo_waste_bytes, G1GCPhaseTimes::MergePSSLABUndoWasteBytes); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, pending_cards, G1GCPhaseTimes::MergePSSPendingCards); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, to_young_gen_cards, G1GCPhaseTimes::MergePSSToYoungGenCards); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, evac_failure_cards, G1GCPhaseTimes::MergePSSEvacFail); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, marked_cards, G1GCPhaseTimes::MergePSSMarked); - - delete pss; - _states[worker_id] = nullptr; + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, copied_bytes, G1GCPhaseTimes::FlushPSSCopiedBytes); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, lab_waste_bytes, G1GCPhaseTimes::FlushPSSLABWasteBytes); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, lab_undo_waste_bytes, G1GCPhaseTimes::FlushPSSLABUndoWasteBytes); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, pending_cards, G1GCPhaseTimes::FlushPSSPendingCards); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, to_young_gen_cards, G1GCPhaseTimes::FlushPSSToYoungGenCards); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, evac_failure_cards, G1GCPhaseTimes::FlushPSSEvacFail); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, marked_cards, G1GCPhaseTimes::FlushPSSMarked); } _flushed = true; } +void G1ParScanThreadStateSet::destroy_worker_states() { + assert(_flushed, "statistics must already be flushed"); + for (uint worker_id = 0; worker_id < _num_workers; ++worker_id) { + delete _states[worker_id]; + _states[worker_id] = nullptr; + } +} + +void G1ParScanThreadStateSet::update_nmethod_regions_to_add(G1NmethodsToAdd* nmethods) { + if (nmethods->number_of_entries() == 0) { + return; + } + + // Take the key set, look which are not yet in the global set, and update the necessary ones. + ResourceMark rm; + GrowableArray regions_to_add = GrowableArray(nmethods->table_size()); + + nmethods->iterate_all([&] (uint& region, void*) { + if (_has_nmethods_to_add.par_set_bit(region, memory_order_relaxed)) { + regions_to_add.push(region); + } + }); + + uint num_regions_to_add = (uint)regions_to_add.length(); + + if (num_regions_to_add == 0) { + return; + } + + uint first_index = _num_nmethod_regions_to_add.fetch_then_add(num_regions_to_add, memory_order_relaxed); + guarantee(first_index + num_regions_to_add <= _g1h->max_num_regions(), "must be"); + + memcpy(&_nmethod_regions_to_add[first_index], regions_to_add.adr_at(0), num_regions_to_add * sizeof(uint)); +} + +void G1ParScanThreadStateSet::par_iterate_nmethod_regions_to_add(G1HeapRegionClosure* cl, + G1HeapRegionClaimer* claimer, + uint worker_id) { + _g1h->par_iterate_regions_array(cl, claimer, _nmethod_regions_to_add, num_nmethod_regions_to_add(), worker_id); +} + void G1ParScanThreadStateSet::record_unused_optional_region(G1HeapRegion* hr) { for (uint worker_index = 0; worker_index < _num_workers; ++worker_index) { G1ParScanThreadState* pss = _states[worker_index]; @@ -676,6 +733,10 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, Kla } } +void G1ParScanThreadState::update_nmethod_regions_to_add() { + _per_thread_states->update_nmethod_regions_to_add(&_nmethods_to_add); +} + void G1ParScanThreadState::initialize_numa_stats() { if (_numa->is_enabled()) { LogTarget(Info, gc, heap, numa) lt; @@ -717,19 +778,25 @@ G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, _g1h(g1h), _collection_set(collection_set), _states(NEW_C_HEAP_ARRAY(G1ParScanThreadState*, num_workers, mtGC)), - _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, collection_set->young_region_length() + 1, mtGC)), + _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, collection_set->num_young_regions() + 1, mtGC)), _num_workers(num_workers), _flushed(false), - _evac_failure_regions(evac_failure_regions) + _evac_failure_regions(evac_failure_regions), + _has_nmethods_to_add(g1h->max_num_regions(), mtGC), + _num_nmethod_regions_to_add(0), + _nmethod_regions_to_add(NEW_C_HEAP_ARRAY(uint, g1h->max_num_regions(), mtGC)) // Conservative length estimation. { for (uint i = 0; i < num_workers; ++i) { _states[i] = nullptr; } - memset(_surviving_young_words_total, 0, (collection_set->young_region_length() + 1) * sizeof(size_t)); + memset(_surviving_young_words_total, 0, (collection_set->num_young_regions() + 1) * sizeof(size_t)); } G1ParScanThreadStateSet::~G1ParScanThreadStateSet() { - assert(_flushed, "thread local state from the per thread states should have been flushed"); + for (uint i = 0; i < _num_workers; i++) { + assert(_states[i] == nullptr, "must be"); + } + FREE_C_HEAP_ARRAY(_nmethod_regions_to_add); FREE_C_HEAP_ARRAY(_states); FREE_C_HEAP_ARRAY(_surviving_young_words_total); } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 3fb080d40be..efecbe1f786 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.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 @@ -37,6 +37,9 @@ #include "gc/shared/taskqueue.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" +#include "runtime/atomic.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/ticks.hpp" class G1CardTable; @@ -44,12 +47,16 @@ class G1CollectionSet; class G1EvacFailureRegions; class G1EvacuationRootClosures; class G1OopStarChunkedList; +class G1ParScanThreadStateSet; class G1PLABAllocator; class G1HeapRegion; class outputStream; +typedef GrowableArrayCHeap G1NmethodSet; +typedef ResizeableHashTable G1NmethodsToAdd; class G1ParScanThreadState : public CHeapObj { G1CollectedHeap* _g1h; + G1ParScanThreadStateSet* _per_thread_states; G1ScannerTasksQueue* _task_queue; G1CardTable* _ct; G1EvacuationRootClosures* _closures; @@ -96,6 +103,9 @@ class G1ParScanThreadState : public CHeapObj { // transferred when flushed. size_t* _obj_alloc_stat; + // The nmethods that were found during code root scan that need to be redistributed. + G1NmethodsToAdd _nmethods_to_add; + // Per-thread evacuation failure data structures. ALLOCATION_FAILURE_INJECTOR_ONLY(size_t _allocation_failure_inject_counter;) @@ -114,6 +124,7 @@ class G1ParScanThreadState : public CHeapObj { public: G1ParScanThreadState(G1CollectedHeap* g1h, + G1ParScanThreadStateSet* per_thread_states, uint worker_id, uint num_workers, G1CollectionSet* collection_set, @@ -243,6 +254,16 @@ public: // An attempt to evacuate "obj" has failed; take necessary steps. oop handle_evacuation_failure_par(oop obj, markWord m, Klass* klass, G1HeapRegionAttr attr, size_t word_sz, bool cause_pinned); + inline void remember_nmethod_into_region(G1HeapRegion* r, nmethod* nm); + // Updates the global set of regions that need updates to the code root set + // later with the ones gathered so far. + void update_nmethod_regions_to_add(); + + inline size_t num_nmethods(uint index) const; + // Iterate nmethods stored for the given region index. + template + inline void iterate_nmethods(uint index, Function fn); + template inline void remember_root_into_optional_region(T* p); template @@ -260,6 +281,10 @@ class G1ParScanThreadStateSet : public StackObj { bool _flushed; G1EvacFailureRegions* _evac_failure_regions; + CHeapBitMap _has_nmethods_to_add; + Atomic _num_nmethod_regions_to_add; + uint* _nmethod_regions_to_add; + public: G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint num_workers, @@ -268,6 +293,15 @@ class G1ParScanThreadStateSet : public StackObj { ~G1ParScanThreadStateSet(); void flush_stats(); + void destroy_worker_states(); + + // Updates the region set that has code root updates with the regions in the given set. + void update_nmethod_regions_to_add(G1NmethodsToAdd* nmethods); + void par_iterate_nmethod_regions_to_add(G1HeapRegionClosure* cl, + G1HeapRegionClaimer* claimer, + uint worker_id); + uint num_nmethod_regions_to_add() const { return _num_nmethod_regions_to_add.load_relaxed(); } + void record_unused_optional_region(G1HeapRegion* hr); #if TASKQUEUE_STATS void print_partial_array_task_stats(); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp index 854c341f720..c42f5f4c4f6 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.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 @@ -70,6 +70,37 @@ inline void G1ParScanThreadState::reset_trim_ticks() { _trim_ticks = Tickspan(); } +inline void G1ParScanThreadState::remember_nmethod_into_region(G1HeapRegion* r, nmethod* nm) { + uint index = r->hrm_index(); + + G1NmethodSet** nmethods = _nmethods_to_add.get(index); + if (nmethods != nullptr) { + (*nmethods)->push(nm); + } else { + G1NmethodSet* new_set = new G1NmethodSet(3); + new_set->push(nm); + bool put_result = _nmethods_to_add.put(index, new_set); + assert(put_result, "must be"); + _nmethods_to_add.maybe_grow(3 /* load_factor */); + } +} + +inline size_t G1ParScanThreadState::num_nmethods(uint region) const { + G1NmethodSet** nmethods = _nmethods_to_add.get(region); + return nmethods != nullptr ? (size_t)(*nmethods)->length() : 0; +} + +template +inline void G1ParScanThreadState::iterate_nmethods(uint index, Function fn) { + G1NmethodSet** nmethods = _nmethods_to_add.get(index); + if (nmethods == nullptr) { + return; + } + for (nmethod* nm : **nmethods) { + fn(nm); + } +} + template inline void G1ParScanThreadState::remember_root_into_optional_region(T* p) { oop o = RawAccess::oop_load(p); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 35211938065..f9b1c182a38 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -60,8 +60,8 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) : _ihop_control(create_ihop_control(&_predictor)), _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)), _cur_pause_start_sec(0.0), - _young_list_desired_length(0), - _young_list_target_length(0), + _desired_num_young_regions(0), + _target_num_young_regions(0), _eden_surv_rate_group(new G1SurvRateGroup()), _survivor_surv_rate_group(new G1SurvRateGroup()), _reserve_factor((double) G1ReservePercent / 100.0), @@ -95,45 +95,45 @@ void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) { _free_regions_at_end_of_collection = _g1h->num_free_regions(); - update_young_length_bounds(); + update_young_regions_bounds(); } void G1Policy::record_young_gc_pause_start() { phase_times()->record_gc_pause_start(); } -class G1YoungLengthPredictor { +class G1NumYoungRegionsPredictor { const double _base_time_ms; const double _base_free_regions; const double _target_pause_time_ms; const G1Policy* const _policy; public: - G1YoungLengthPredictor(double base_time_ms, - double base_free_regions, - double target_pause_time_ms, - const G1Policy* policy) : + G1NumYoungRegionsPredictor(double base_time_ms, + double base_free_regions, + double target_pause_time_ms, + const G1Policy* policy) : _base_time_ms(base_time_ms), _base_free_regions(base_free_regions), _target_pause_time_ms(target_pause_time_ms), _policy(policy) {} - bool will_fit(uint young_length) const { - if (young_length >= _base_free_regions) { + bool will_fit(uint num_young_regions) const { + if (num_young_regions >= _base_free_regions) { // end condition 1: not enough space for the young regions return false; } size_t bytes_to_copy = 0; - const double copy_time_ms = _policy->predict_eden_copy_time_ms(young_length, &bytes_to_copy); - const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(young_length); + const double copy_time_ms = _policy->predict_eden_copy_time_ms(num_young_regions, &bytes_to_copy); + const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(num_young_regions); const double pause_time_ms = _base_time_ms + copy_time_ms + young_other_time_ms; if (pause_time_ms > _target_pause_time_ms) { // end condition 2: prediction is over the target pause time return false; } - const size_t free_bytes = (_base_free_regions - young_length) * G1HeapRegion::GrainBytes; + const size_t free_bytes = (_base_free_regions - num_young_regions) * G1HeapRegion::GrainBytes; // When copying, we will likely need more bytes free than is live in the region. // Add some safety margin to factor in the confidence of our guess, and the @@ -167,61 +167,62 @@ void G1Policy::record_new_heap_size(uint new_number_of_regions) { _ihop_control->update_target_occupancy(new_number_of_regions * G1HeapRegion::GrainBytes); } -uint G1Policy::calculate_desired_eden_length_by_mmu() const { - assert(use_adaptive_young_list_length(), "precondition"); +uint G1Policy::calculate_desired_num_eden_regions_by_mmu() const { + assert(use_adaptive_num_young_regions(), "precondition"); double now_sec = os::elapsedTime(); double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0; double alloc_rate_ms = _analytics->predict_alloc_rate_ms(); return (uint) ceil(alloc_rate_ms * when_ms); } -void G1Policy::update_young_length_bounds() { +void G1Policy::update_young_regions_bounds() { assert(!Universe::is_fully_initialized() || SafepointSynchronize::is_at_safepoint(), "must be"); bool for_young_only_phase = collector_state()->is_in_young_only_phase(); - update_young_length_bounds(_analytics->predict_pending_cards(for_young_only_phase), + update_young_regions_bounds(_analytics->predict_pending_cards(for_young_only_phase), _analytics->predict_card_rs_length(for_young_only_phase), _analytics->predict_code_root_rs_length(for_young_only_phase)); } -void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { - uint old_young_list_target_length = young_list_target_length(); +void G1Policy::update_young_regions_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { + uint old_target_num_young_regions = target_num_young_regions(); - uint min_young_length_by_sizer = _young_gen_sizer.min_desired_young_length(); - uint max_young_length_by_sizer = _young_gen_sizer.max_desired_young_length(); + uint min_num_young_regions_by_sizer = _young_gen_sizer.min_desired_num_regions(); + uint max_num_young_regions_by_sizer = _young_gen_sizer.max_desired_num_regions(); - if (max_young_length_by_sizer < min_young_length_by_sizer) { - // This can happen due to races with heap_size_changed() at mutator time. Do not update the young gen - // lengths. Will be updated on the next regular call anyway. + if (max_num_young_regions_by_sizer < min_num_young_regions_by_sizer) { + // This can happen due to races with heap_size_changed() at mutator time. Do not update the + // young regions. Will be updated on the next regular call anyway. assert(!SafepointSynchronize::is_at_safepoint(), "must be"); return; } - uint new_young_list_desired_length = calculate_young_desired_length(pending_cards, - card_rs_length, - code_root_rs_length, - min_young_length_by_sizer, - max_young_length_by_sizer); - uint new_young_list_target_length = calculate_young_target_length(new_young_list_desired_length, min_young_length_by_sizer); + uint new_desired_num_young_regions = calculate_desired_num_young_regions(pending_cards, + card_rs_length, + code_root_rs_length, + min_num_young_regions_by_sizer, + max_num_young_regions_by_sizer); + uint new_target_num_young_regions = calculate_target_num_young_regions(new_desired_num_young_regions, + min_num_young_regions_by_sizer); - log_trace(gc, ergo, heap)("Young list length update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", + log_trace(gc, ergo, heap)("Young num regions update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", pending_cards, card_rs_length, - old_young_list_target_length, - new_young_list_desired_length, - new_young_list_target_length); + old_target_num_young_regions, + new_desired_num_young_regions, + new_target_num_young_regions); // Write back. This is not an attempt to control visibility order to other threads - // here; all the revising of the young gen length are best effort to keep pause time. + // here; all the revising of the number of young regions are best effort to keep pause time. // E.g. we could be "too late" revising young gen upwards to avoid GC because // there is some time left, or some threads could get different values for stopping // allocation. // That is "fine" - at most this will schedule a GC (hopefully only a little) too // early or too late. - _young_list_desired_length.store_relaxed(new_young_list_desired_length); - _young_list_target_length.store_relaxed(new_young_list_target_length); + _desired_num_young_regions.store_relaxed(new_desired_num_young_regions); + _target_num_young_regions.store_relaxed(new_target_num_young_regions); } -// Calculates desired young gen length. It is calculated from: +// Calculates desired number of young regions. It is calculated from: // // - sizer min/max bounds on young gen // - pause time goal for whole young gen evacuation @@ -236,40 +237,40 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l // value smaller than what is already allocated or what can actually be allocated. // This return value is only an expectation. // -uint G1Policy::calculate_young_desired_length(size_t pending_cards, - size_t card_rs_length, - size_t code_root_rs_length, - uint min_young_length_by_sizer, - uint max_young_length_by_sizer) const { +uint G1Policy::calculate_desired_num_young_regions(size_t pending_cards, + size_t card_rs_length, + size_t code_root_rs_length, + uint min_num_young_regions_by_sizer, + uint max_num_young_regions_by_sizer) const { - assert(min_young_length_by_sizer >= 1, "invariant"); - assert(max_young_length_by_sizer >= min_young_length_by_sizer, "invariant"); + assert(min_num_young_regions_by_sizer >= 1, "invariant"); + assert(max_num_young_regions_by_sizer >= min_num_young_regions_by_sizer, "invariant"); // Calculate the absolute and desired min bounds first. // This is how many survivor regions we already have. - const uint survivor_length = _g1h->survivor_regions_count(); + const uint num_survivor_regions = _g1h->survivor_regions_count(); // Size of the already allocated young gen. - const uint allocated_young_length = _g1h->young_regions_count(); - // This is the absolute minimum young length that we can return. Ensure that we + const uint allocated_num_young_regions = _g1h->young_regions_count(); + // This is the absolute minimum number of young regions that we can return. Ensure that we // don't go below any user-defined minimum bound. Also, we must have at least // one eden region, to ensure progress. But when revising during the ensuing // mutator phase we might have already allocated more than either of those, in // which case use that. - uint absolute_min_young_length = MAX3(min_young_length_by_sizer, - survivor_length + 1, - allocated_young_length); + uint absolute_min_num_young_regions = MAX3(min_num_young_regions_by_sizer, + num_survivor_regions + 1, + allocated_num_young_regions); // Calculate the absolute max bounds. After evac failure or when revising the - // young length we might have exceeded absolute min length or absolute_max_length, + // number of young regions we might have exceeded absolute min or max_num_young_regions, // so adjust the result accordingly. - uint absolute_max_young_length = MAX2(max_young_length_by_sizer, absolute_min_young_length); + uint absolute_max_num_young_regions = MAX2(max_num_young_regions_by_sizer, absolute_min_num_young_regions); - uint desired_eden_length_by_mmu = 0; - uint desired_eden_length_by_pause = 0; + uint desired_num_eden_regions_by_mmu = 0; + uint desired_num_eden_regions_by_pause = 0; - uint desired_young_length = 0; - if (use_adaptive_young_list_length()) { - desired_eden_length_by_mmu = calculate_desired_eden_length_by_mmu(); + uint desired_num_young_regions = 0; + if (use_adaptive_num_young_regions()) { + desired_num_eden_regions_by_mmu = calculate_desired_num_eden_regions_by_mmu(); double base_time_ms = predict_base_time_ms(pending_cards, card_rs_length, code_root_rs_length); double retained_time_ms = predict_retained_regions_evac_time(); @@ -278,55 +279,56 @@ uint G1Policy::calculate_young_desired_length(size_t pending_cards, log_trace(gc, ergo, heap)("Predicted total base time: total %f base_time %f retained_time %f", total_time_ms, base_time_ms, retained_time_ms); - desired_eden_length_by_pause = - calculate_desired_eden_length_by_pause(total_time_ms, - absolute_min_young_length - survivor_length, - absolute_max_young_length - survivor_length); + desired_num_eden_regions_by_pause = + calculate_desired_num_eden_regions_by_pause(total_time_ms, + absolute_min_num_young_regions - num_survivor_regions, + absolute_max_num_young_regions - num_survivor_regions); // Incorporate MMU concerns; assume that it overrides the pause time // goal, as the default value has been chosen to effectively disable it. - uint desired_eden_length = MAX2(desired_eden_length_by_pause, - desired_eden_length_by_mmu); + uint desired_num_eden_regions = MAX2(desired_num_eden_regions_by_pause, + desired_num_eden_regions_by_mmu); - desired_young_length = desired_eden_length + survivor_length; + desired_num_young_regions = desired_num_eden_regions + num_survivor_regions; } else { // The user asked for a fixed young gen so we'll fix the young gen // whether the next GC is young or mixed. - desired_young_length = min_young_length_by_sizer; + desired_num_young_regions = min_num_young_regions_by_sizer; } - // Clamp to absolute min/max after we determined desired lengths. - desired_young_length = clamp(desired_young_length, absolute_min_young_length, absolute_max_young_length); + // Clamp to absolute min/max after we determined desired number of regions. + desired_num_young_regions = clamp(desired_num_young_regions, absolute_min_num_young_regions, absolute_max_num_young_regions); - log_trace(gc, ergo, heap)("Young desired length %u " - "survivor length %u " - "allocated young length %u " - "absolute min young length %u " - "absolute max young length %u " - "desired eden length by mmu %u " - "desired eden length by pause %u ", - desired_young_length, survivor_length, - allocated_young_length, absolute_min_young_length, - absolute_max_young_length, desired_eden_length_by_mmu, - desired_eden_length_by_pause); + log_trace(gc, ergo, heap)("Desired young regions %u " + "survivor regions %u " + "allocated young regions %u " + "absolute min young regions %u " + "absolute max young regions %u " + "desired eden regions by mmu %u " + "desired eden regions by pause %u ", + desired_num_young_regions, num_survivor_regions, + allocated_num_young_regions, absolute_min_num_young_regions, + absolute_max_num_young_regions, desired_num_eden_regions_by_mmu, + desired_num_eden_regions_by_pause); - assert(desired_young_length >= allocated_young_length, "must be"); - return desired_young_length; + assert(desired_num_young_regions >= allocated_num_young_regions, "must be"); + return desired_num_young_regions; } -// Limit the desired (wished) young length by current free regions. If the request +// Limit the desired (wished) number of young regions by current free regions. If the request // can be satisfied without using up reserve regions, do so, otherwise eat into // the reserve, giving away at most what the heap sizer allows. -uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const { - uint allocated_young_length = _g1h->young_regions_count(); +uint G1Policy::calculate_target_num_young_regions(uint desired_num_young_regions, + uint min_num_young_regions_by_sizer) const { + uint num_young_regions = _g1h->young_regions_count(); uint receiving_additional_eden; - if (allocated_young_length >= desired_young_length) { + if (num_young_regions >= desired_num_young_regions) { // Already used up all we actually want (may happen as G1 revises the - // young list length concurrently). Do not allow more, potentially resulting in GC. + // number of young regions concurrently). Do not allow more, potentially resulting in GC. receiving_additional_eden = 0; - log_trace(gc, ergo, heap)("Young target length: Already used up desired young %u allocated %u", - desired_young_length, - allocated_young_length); + log_trace(gc, ergo, heap)("Target young regions: Already used up desired young regions %u allocated young regions %u", + desired_num_young_regions, + num_young_regions); } else { // Now look at how many free regions are there currently, and the heap reserve. // We will try our best not to "eat" into the reserve as long as we can. If we @@ -336,57 +338,57 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min // The heap reserve needs to be snapshotted for consistent use in the following. // It can be concurrently modified by the mutator as it expands the heap. It can // only increase at that time, so this is a conservative snapshot. So at worst this - // method will return a too small young gen length in that case. + // method will return a too small number of young regions in that case. uint reserve_regions = _reserve_regions.load_relaxed(); - uint max_to_eat_into_reserve = MIN2(min_young_length_by_sizer, + uint max_to_eat_into_reserve = MIN2(min_num_young_regions_by_sizer, (reserve_regions + 1) / 2); - log_trace(gc, ergo, heap)("Young target length: Common " + log_trace(gc, ergo, heap)("Target young regions: Common " "free regions at end of collection %u " - "desired young length %u " + "desired number of young regions %u " "reserve region %u " "max to eat into reserve %u", _free_regions_at_end_of_collection, - desired_young_length, + desired_num_young_regions, reserve_regions, max_to_eat_into_reserve); uint survivor_regions_count = _g1h->survivor_regions_count(); - uint desired_eden_length = desired_young_length - survivor_regions_count; - uint allocated_eden_length = allocated_young_length - survivor_regions_count; + uint desired_num_eden_regions = desired_num_young_regions - survivor_regions_count; + uint num_eden_regions = num_young_regions - survivor_regions_count; if (_free_regions_at_end_of_collection <= reserve_regions) { - // Fully eat (or already eating) into the reserve, hand back at most absolute_min_length regions. + // Fully eat (or already eating) into the reserve. uint receiving_eden = MIN3(_free_regions_at_end_of_collection, - desired_eden_length, - max_to_eat_into_reserve); + desired_num_eden_regions, + max_to_eat_into_reserve); // Ensure that we provision for at least one Eden region. receiving_eden = MAX2(receiving_eden, 1u); // We could already have allocated more regions than what we could get // above. - receiving_additional_eden = allocated_eden_length < receiving_eden ? - receiving_eden - allocated_eden_length : 0; + receiving_additional_eden = num_eden_regions < receiving_eden ? + receiving_eden - num_eden_regions : 0; - log_trace(gc, ergo, heap)("Young target length: Fully eat into reserve " + log_trace(gc, ergo, heap)("Target young regions: Fully eat into reserve " "receiving eden %u receiving additional eden %u", receiving_eden, receiving_additional_eden); - } else if (_free_regions_at_end_of_collection < (desired_eden_length + reserve_regions)) { + } else if (_free_regions_at_end_of_collection < (desired_num_eden_regions + reserve_regions)) { // Partially eat into the reserve, at most max_to_eat_into_reserve regions. uint free_outside_reserve = _free_regions_at_end_of_collection - reserve_regions; - assert(free_outside_reserve < desired_eden_length, + assert(free_outside_reserve < desired_num_eden_regions, "must be %u %u", - free_outside_reserve, desired_eden_length); + free_outside_reserve, desired_num_eden_regions); - uint receiving_within_reserve = MIN2(desired_eden_length - free_outside_reserve, + uint receiving_within_reserve = MIN2(desired_num_eden_regions - free_outside_reserve, max_to_eat_into_reserve); uint receiving_eden = free_outside_reserve + receiving_within_reserve; // Again, we could have already allocated more than we could get. - receiving_additional_eden = allocated_eden_length < receiving_eden ? - receiving_eden - allocated_eden_length : 0; + receiving_additional_eden = num_eden_regions < receiving_eden ? + receiving_eden - num_eden_regions : 0; - log_trace(gc, ergo, heap)("Young target length: Partially eat into reserve " + log_trace(gc, ergo, heap)("Target young regions: Partially eat into reserve " "free outside reserve %u " "receiving within reserve %u " "receiving eden %u " @@ -395,116 +397,116 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min receiving_eden, receiving_additional_eden); } else { // No need to use the reserve. - receiving_additional_eden = desired_young_length - allocated_young_length; - log_trace(gc, ergo, heap)("Young target length: No need to use reserve " + receiving_additional_eden = desired_num_young_regions - num_young_regions; + log_trace(gc, ergo, heap)("Target young regions: No need to use reserve " "receiving additional eden %u", receiving_additional_eden); } } - uint target_young_length = allocated_young_length + receiving_additional_eden; + uint target_num_young_regions = num_young_regions + receiving_additional_eden; - assert(target_young_length >= allocated_young_length, "must be"); + assert(target_num_young_regions >= num_young_regions, "must be"); - log_trace(gc, ergo, heap)("Young target length: " - "young target length %u " - "allocated young length %u " + log_trace(gc, ergo, heap)("Target num young regions: " + "target num young regions %u " + "allocated number of young regions %u " "received additional eden %u", - target_young_length, allocated_young_length, + target_num_young_regions, num_young_regions, receiving_additional_eden); - return target_young_length; + return target_num_young_regions; } -uint G1Policy::calculate_desired_eden_length_by_pause(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { +uint G1Policy::calculate_desired_num_eden_regions_by_pause(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { if (!next_gc_should_be_mixed()) { - return calculate_desired_eden_length_before_young_only(base_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_young_only(base_time_ms, + min_num_eden_regions, + max_num_eden_regions); } else { - return calculate_desired_eden_length_before_mixed(base_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_mixed(base_time_ms, + min_num_eden_regions, + max_num_eden_regions); } } -uint G1Policy::calculate_desired_eden_length_before_young_only(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { - assert(use_adaptive_young_list_length(), "pre-condition"); +uint G1Policy::calculate_desired_num_eden_regions_before_young_only(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { + assert(use_adaptive_num_young_regions(), "pre-condition"); - assert(min_eden_length <= max_eden_length, "must be %u %u", min_eden_length, max_eden_length); + assert(min_num_eden_regions <= max_num_eden_regions, "must be %u %u", min_num_eden_regions, max_num_eden_regions); - // Here, we will make sure that the shortest young length that + // Here, we will make sure that the smallest number of eden regions that // makes sense fits within the target pause time. - G1YoungLengthPredictor p(base_time_ms, - _free_regions_at_end_of_collection, - _mmu_tracker->max_gc_time() * 1000.0, - this); - if (p.will_fit(min_eden_length)) { - // The shortest young length will fit into the target pause time; - // we'll now check whether the absolute maximum number of young - // regions will fit in the target pause time. If not, we'll do - // a binary search between min_young_length and max_young_length. - if (p.will_fit(max_eden_length)) { - // The maximum young length will fit into the target pause time. - // We are done so set min young length to the maximum length (as - // the result is assumed to be returned in min_young_length). - min_eden_length = max_eden_length; + G1NumYoungRegionsPredictor p(base_time_ms, + _free_regions_at_end_of_collection, + _mmu_tracker->max_gc_time() * 1000.0, + this); + if (p.will_fit(min_num_eden_regions)) { + // The smallest number of eden regions will fit into the target pause time; + // we'll now check whether the absolute maximum number of young regions will fit + // in the target pause time. If not, we'll do a binary search between + // min_num_eden_regions and max_num_eden_regions. + if (p.will_fit(max_num_eden_regions)) { + // The maximum number of eden regions will fit into the target pause time. + // We are done, so set min_num_eden_regions to max_num_eden_regions (as the result is + // assumed to be returned in min_num_eden_regions). + min_num_eden_regions = max_num_eden_regions; } else { - // The maximum possible number of young regions will not fit within + // The maximum possible number of eden regions will not fit within // the target pause time so we'll search for the optimal - // length. The loop invariants are: + // number of eden regions. The loop invariants are: // - // min_young_length < max_young_length - // min_young_length is known to fit into the target pause time - // max_young_length is known not to fit into the target pause time + // min_num_eden_regions < max_num_eden_regions + // min_num_eden_regions is known to fit into the target pause time + // max_num_eden_regions is known not to fit into the target pause time // // Going into the loop we know the above hold as we've just // checked them. Every time around the loop we check whether - // the middle value between min_young_length and - // max_young_length fits into the target pause time. If it + // the middle value between min_num_eden_regions and + // max_num_eden_regions fits into the target pause time. If it // does, it becomes the new min. If it doesn't, it becomes // the new max. This way we maintain the loop invariants. - assert(min_eden_length < max_eden_length, "invariant"); - uint diff = (max_eden_length - min_eden_length) / 2; + precond(min_num_eden_regions < max_num_eden_regions); + uint diff = (max_num_eden_regions - min_num_eden_regions) / 2; while (diff > 0) { - uint eden_length = min_eden_length + diff; - if (p.will_fit(eden_length)) { - min_eden_length = eden_length; + uint num_eden_regions = min_num_eden_regions + diff; + if (p.will_fit(num_eden_regions)) { + min_num_eden_regions = num_eden_regions; } else { - max_eden_length = eden_length; + max_num_eden_regions = num_eden_regions; } - assert(min_eden_length < max_eden_length, "invariant"); - diff = (max_eden_length - min_eden_length) / 2; + postcond(min_num_eden_regions < max_num_eden_regions); + diff = (max_num_eden_regions - min_num_eden_regions) / 2; } - // The results is min_young_length which, according to the + // The result is min_num_eden_regions which, according to the // loop invariants, should fit within the target pause time. // These are the post-conditions of the binary search above: - assert(min_eden_length < max_eden_length, - "otherwise we should have discovered that max_eden_length " + assert(min_num_eden_regions < max_num_eden_regions, + "otherwise we should have discovered that max_num_eden_regions " "fits into the pause target and not done the binary search"); - assert(p.will_fit(min_eden_length), - "min_eden_length, the result of the binary search, should " + assert(p.will_fit(min_num_eden_regions), + "min_num_eden_regions, the result of the binary search, should " "fit into the pause target"); - assert(!p.will_fit(min_eden_length + 1), - "min_eden_length, the result of the binary search, should be " - "optimal, so no larger length should fit into the pause target"); + assert(!p.will_fit(min_num_eden_regions + 1), + "min_num_eden_regions, the result of the binary search, should be " + "optimal, so no larger number of eden regions should fit into the pause target"); } } else { - // Even the minimum length doesn't fit into the pause time - // target, return it as the result nevertheless. + // Even the minimum number of eden regions does not fit into the target pause time, + // return it as the result nevertheless. } - return min_eden_length; + return min_num_eden_regions; } -uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { +uint G1Policy::calculate_desired_num_eden_regions_before_mixed(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { uint min_marking_candidates = MIN2(calc_min_old_cset_length(candidates()->last_marking_candidates_length()), candidates()->from_marking_groups().num_regions()); double predicted_region_evac_time_ms = base_time_ms; @@ -517,9 +519,9 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, selected_candidates += gr->length(); } - return calculate_desired_eden_length_before_young_only(predicted_region_evac_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_young_only(predicted_region_evac_time_ms, + min_num_eden_regions, + max_num_eden_regions); } double G1Policy::predict_survivor_regions_evac_time() const { @@ -572,10 +574,10 @@ G1GCPhaseTimes* G1Policy::phase_times() const { return _phase_times; } -void G1Policy::revise_young_list_target_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { - guarantee(use_adaptive_young_list_length(), "should not call this otherwise" ); +void G1Policy::revise_target_num_young_regions(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { + guarantee(use_adaptive_num_young_regions(), "should not call this otherwise" ); - update_young_length_bounds(pending_cards, card_rs_length, code_root_rs_length); + update_young_regions_bounds(pending_cards, card_rs_length, code_root_rs_length); } void G1Policy::record_full_collection_start() { @@ -601,7 +603,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { _free_regions_at_end_of_collection = _g1h->num_free_regions(); _survivor_surv_rate_group->reset(); - update_young_length_bounds(); + update_young_regions_bounds(); record_pause(Pause::Full, start_time_sec, end_sec); } @@ -641,9 +643,9 @@ void G1Policy::record_dirtying_stats(double last_mutator_start_dirty_ms, double yield_duration_ms, size_t next_pending_cards_from_gc, size_t next_to_collection_set_cards) { - assert(SafepointSynchronize::is_at_safepoint() || G1ReviseYoungLength_lock->is_locked(), + assert(SafepointSynchronize::is_at_safepoint() || G1ReviseNumYoungRegions_lock->is_locked(), "must be (at safepoint %s locked %s)", - BOOL_TO_STR(SafepointSynchronize::is_at_safepoint()), BOOL_TO_STR(G1ReviseYoungLength_lock->is_locked())); + BOOL_TO_STR(SafepointSynchronize::is_at_safepoint()), BOOL_TO_STR(G1ReviseNumYoungRegions_lock->is_locked())); // Record mutator's card logging rate. // Unlike above for conc-refine rate, here we should not require a @@ -672,8 +674,8 @@ void G1Policy::record_dirtying_stats(double last_mutator_start_dirty_ms, _to_collection_set_cards = next_to_collection_set_cards; } -bool G1Policy::should_retain_evac_failed_region(uint index) const { - size_t live_bytes = _g1h->region_at(index)->live_bytes(); +bool G1Policy::should_retain_evac_failed_region(G1HeapRegion* r) const { + size_t live_bytes = r->live_bytes(); size_t threshold = G1RetainRegionLiveThresholdPercent * G1HeapRegion::GrainBytes / 100; return live_bytes < threshold; } @@ -693,7 +695,7 @@ void G1Policy::record_young_collection_start() { record_pause_start_time(); // We only need to do this here as the policy will only be applied // to the GC we're about to start. so, no point is calculating this - // every time we calculate / recalculate the target young length. + // every time we calculate / recalculate the target number of young regions. update_survivors_policy(); assert(max_survivor_regions() + _g1h->num_used_regions() <= _g1h->max_num_regions(), @@ -849,13 +851,13 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation if (update_stats) { // We maintain the invariant that all objects allocated by mutator // threads will be allocated out of eden regions. So, we can use - // the eden region number allocated since the previous GC to - // calculate the application's allocate rate. The only exception + // the number of eden regions allocated since the previous GC to + // calculate the application's allocation rate. The only exception // to that is humongous objects that are allocated separately. But // given that humongous object allocations do not really affect // either the pause's duration nor when the next pause will take // place we can safely ignore them here. - uint regions_allocated = _collection_set->eden_region_length(); + uint regions_allocated = _collection_set->num_eden_regions(); double alloc_rate_ms = (double) regions_allocated / app_time_ms; _analytics->report_alloc_rate_ms(alloc_rate_ms); @@ -911,7 +913,7 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation } // Update prediction for copy cost per byte - size_t copied_bytes = p->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSCopiedBytes); + size_t copied_bytes = p->sum_thread_work_items(G1GCPhaseTimes::FlushPSS, G1GCPhaseTimes::FlushPSSCopiedBytes); if (copied_bytes > 0) { double avg_copy_time = average_time_ms(G1GCPhaseTimes::ObjCopy) + average_time_ms(G1GCPhaseTimes::OptObjCopy); @@ -919,14 +921,14 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation _analytics->report_cost_per_byte_ms(cost_per_byte_ms, is_young_only_pause); } - if (_collection_set->young_region_length() > 0) { + if (_collection_set->num_young_regions() > 0) { _analytics->report_young_other_cost_per_region_ms(young_other_time_ms() / - _collection_set->young_region_length()); + _collection_set->num_young_regions()); } - if (_collection_set->initial_old_region_length() > 0) { + if (_collection_set->num_initial_old_regions() > 0) { _analytics->report_non_young_other_cost_per_region_ms(non_young_other_time_ms() / - _collection_set->initial_old_region_length()); + _collection_set->num_initial_old_regions()); } _analytics->report_constant_other_time_ms(constant_other_time_ms(pause_time_ms)); @@ -948,8 +950,8 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation mutator_end_time, pending_cards_from_refinement_table, yield_duration_ms, - phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSPendingCards), - phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSToYoungGenCards)); + phase_times()->sum_thread_work_items(G1GCPhaseTimes::FlushPSS, G1GCPhaseTimes::FlushPSSPendingCards), + phase_times()->sum_thread_work_items(G1GCPhaseTimes::FlushPSS, G1GCPhaseTimes::FlushPSSToYoungGenCards)); } if (collector_state()->is_in_prepare_mixed_gc()) { @@ -992,7 +994,7 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation // 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(); + update_young_regions_bounds(); // Take snapshots of these values here as update_ihop_prediction // may complete the concurrent cycle and reset the values. @@ -1068,11 +1070,11 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s, // The second clause prevents skewing the IHOP prediction with (typically) degenerate // back-to-back young-gen-size samples. if (this_gc_was_young_only && mutator_time_s > min_valid_time) { - // IHOP control wants to know the expected young gen length if it were not - // restrained by the heap reserve. Using the actual length would make the + // IHOP control wants to know the expected number of young regions if it were not + // restrained by the heap reserve. Using the current number of regions would make the // prediction too small and the limit the young gen every time we get to the // predicted target occupancy. - size_t young_gen_size = young_list_desired_length() * G1HeapRegion::GrainBytes; + size_t young_gen_size = desired_num_young_regions() * G1HeapRegion::GrainBytes; _ihop_control->record_expected_young_gen_size(young_gen_size); report = true; @@ -1188,7 +1190,7 @@ double G1Policy::predict_region_code_root_scan_time(G1HeapRegion* hr, bool for_y } bool G1Policy::should_allocate_mutator_region() const { - if (_g1h->young_regions_count() < young_list_target_length()) { + if (_g1h->young_regions_count() < target_num_young_regions()) { return true; } @@ -1205,8 +1207,8 @@ bool G1Policy::should_expand_on_mutator_allocation() const { return !is_init_completed(); } -bool G1Policy::use_adaptive_young_list_length() const { - return _young_gen_sizer.use_adaptive_young_list_length(); +bool G1Policy::use_adaptive_num_young_regions() const { + return _young_gen_sizer.use_adaptive_num_young_regions(); } size_t G1Policy::estimate_used_young_bytes_locked() const { @@ -1231,7 +1233,7 @@ void G1Policy::print_age_table() { // Calculates survivor space parameters. void G1Policy::update_survivors_policy() { double max_survivor_regions_d = - (double)young_list_target_length() / (double) SurvivorRatio; + (double)target_num_young_regions() / (double) SurvivorRatio; // Calculate desired survivor size based on desired max survivor regions (unconstrained // by remaining heap). Otherwise we may cause undesired promotions as we are @@ -1251,9 +1253,10 @@ void G1Policy::update_survivors_policy() { } bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) { - // We actually check whether we are marking here and not if we are in a - // reclamation phase. This means that we will schedule a concurrent mark - // even while we are still in the process of reclaiming memory. + assert_at_safepoint_on_vm_thread(); + // Check whether a concurrent cycle is active, do not include the + // reclamation/mixed phase. This means that we can schedule a concurrent cycle + // even while in the mixed phase. bool during_cycle = collector_state()->is_in_concurrent_cycle(); if (!during_cycle) { log_debug(gc, ergo)("Request concurrent cycle initiation (requested by GC cause). " @@ -1440,7 +1443,7 @@ bool G1Policy::try_get_available_bytes_estimate(size_t& available_bytes) const { size_t used_bytes = estimate_used_young_bytes_locked(); Heap_lock->unlock(); - size_t young_bytes = young_list_target_length() * G1HeapRegion::GrainBytes; + size_t young_bytes = target_num_young_regions() * G1HeapRegion::GrainBytes; available_bytes = young_bytes - MIN2(young_bytes, used_bytes); return true; } else { @@ -1532,7 +1535,7 @@ void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { } stop_adding_survivor_regions(); - // Don't clear the survivor list handles until the start of + // Don't clear the survivor region tracking until the start of // the next evacuation pause - we need it in order to re-tag // the survivor regions from this evacuation pause as 'young' // at the start of the next. diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 3cfd54c8c94..e09a76397cc 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -79,11 +79,11 @@ class G1Policy: public CHeapObj { double _cur_pause_start_sec; - // Desired young gen length without taking actually available free regions into + // Desired number of young regions without taking actually available free regions into // account. - Atomic _young_list_desired_length; - // Actual target length given available free memory. - Atomic _young_list_target_length; + Atomic _desired_num_young_regions; + // Actual target number of young regions given available free memory. + Atomic _target_num_young_regions; // The survivor rate groups below must be initialized after the predictor because they // indirectly use it through the "this" object passed to their constructor. @@ -193,44 +193,45 @@ private: // Lazily initialized mutable G1GCPhaseTimes* _phase_times; - // Updates the internal young gen maximum and target and desired lengths. + // Updates the internal young gen maximum and target and desired number of young regions. // If no parameters are passed, predict pending cards, card set remset length and // code root remset length using the prediction model. - void update_young_length_bounds(); - void update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); + void update_young_regions_bounds(); + void update_young_regions_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); - // Calculate and return the minimum desired eden length based on the MMU target. - uint calculate_desired_eden_length_by_mmu() const; + // Calculate and return the minimum desired number of eden regions based on the MMU target. + uint calculate_desired_num_eden_regions_by_mmu() const; - // Calculate the desired eden length meeting the pause time goal. - // Min_eden_length and max_eden_length are the bounds + // Calculate the desired number of eden regions meeting the pause time goal. + // min_num_eden_regions and max_num_eden_regions are the bounds // (inclusive) within which eden can grow. - uint calculate_desired_eden_length_by_pause(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_by_pause(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculate the desired eden length that can fit into the pause time + // Calculate the desired number of eden regions that can fit into the pause time // goal before young only gcs. - uint calculate_desired_eden_length_before_young_only(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_before_young_only(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculates the desired eden length before mixed gc so that after adding the + // Calculates the desired number of eden regions before mixed gc so that after adding the // minimum amount of old gen regions from the collection set, the eden fits into // the pause time goal. - uint calculate_desired_eden_length_before_mixed(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_before_mixed(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculate desired young length based on current situation without taking actually + // Calculate desired number of young regions based on current situation without taking actually // available free regions into account. - uint calculate_young_desired_length(size_t pending_cards, - size_t card_rs_length, - size_t code_root_rs_length, - uint min_young_length_by_sizer, - uint max_young_length_by_sizer) const; - // Limit the given desired young length to available free regions. - uint calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const; + uint calculate_desired_num_young_regions(size_t pending_cards, + size_t card_rs_length, + size_t code_root_rs_length, + uint min_num_young_regions_by_sizer, + uint max_num_young_regions_by_sizer) const; + // Limit the given desired number of young regions to available free regions. + uint calculate_target_num_young_regions(uint desired_num_young_regions, + uint min_num_young_regions_by_sizer) const; double predict_survivor_regions_evac_time() const; double predict_retained_regions_evac_time() const; @@ -283,10 +284,10 @@ public: G1GCPhaseTimes* phase_times() const; - // Check the current value of the young list RSet length and + // Check the current value of the young generation RSet length and // compare it against the last prediction. If the current value is - // higher, recalculate the young list target length prediction. - void revise_young_list_target_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); + // higher, recalculate the target number of young regions prediction. + void revise_target_num_young_regions(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); // This should be called after the heap is resized. void record_new_heap_size(uint new_number_of_regions); @@ -334,9 +335,8 @@ private: 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 - // is called during a safepoint when the test whether a cycle is in - // progress or not is stable. + // new cycle, as long as we are not already in one. It is called + // at a safepoint. bool force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause); // Decide whether this garbage collection pause should be a concurrent start @@ -347,13 +347,13 @@ public: // This must be called at the very beginning of an evacuation pause. void decide_on_concurrent_start_pause(); - uint young_list_desired_length() const { return _young_list_desired_length.load_relaxed(); } - uint young_list_target_length() const { return _young_list_target_length.load_relaxed(); } + uint desired_num_young_regions() const { return _desired_num_young_regions.load_relaxed(); } + uint target_num_young_regions() const { return _target_num_young_regions.load_relaxed(); } bool should_allocate_mutator_region() const; bool should_expand_on_mutator_allocation() const; - bool use_adaptive_young_list_length() const; + bool use_adaptive_num_young_regions() const; // Try to get an estimate of the currently available bytes in the young gen. This // operation considers itself low-priority: if other threads need the resources @@ -389,10 +389,7 @@ public: size_t next_pending_cards_from_gc, size_t next_to_collection_set_cards); - bool should_retain_evac_failed_region(G1HeapRegion* r) const { - return should_retain_evac_failed_region(r->hrm_index()); - } - bool should_retain_evac_failed_region(uint index) const; + bool should_retain_evac_failed_region(G1HeapRegion* r) const; private: // diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index df76147f4b1..4db2dbd2287 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -44,6 +44,8 @@ struct G1RegionMarkStats { Atomic _live_words; Atomic _incoming_refs; + G1RegionMarkStats() : _live_words(0), _incoming_refs(0) { } + // Clear all members. void clear() { _live_words.store_relaxed(0); @@ -121,6 +123,16 @@ public: cur->_stats._live_words.store_relaxed(cur->_stats.live_words() + live_words); } + void verify_no_mark_stats_for(uint region_idx) { + uint const cache_idx = hash(region_idx); + G1RegionMarkStatsCacheEntry* const cur = &_cache[cache_idx]; + if (cur->_region_idx != region_idx) { + return; + } + assert(cur->_stats.incoming_refs() == 0, "must be"); + assert(cur->_stats.live_words() == 0, "must be"); + } + void inc_incoming_refs(uint region_idx) { G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx); // This method is only ever called single-threaded, so we do not need atomic diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp index 5e37c7fa5a1..6ecaaf78e5e 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp @@ -28,7 +28,6 @@ #include "gc/shared/gc_globals.hpp" #include "memory/allocation.inline.hpp" #include "memory/reservedSpace.hpp" -#include "nmt/memTracker.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" @@ -46,8 +45,6 @@ G1RegionToSpaceMapper::G1RegionToSpaceMapper(ReservedSpace rs, _memory_tag(mem_tag) { guarantee(is_power_of_2(page_size), "must be"); guarantee(is_power_of_2(region_granularity), "must be"); - - MemTracker::record_virtual_memory_tag(rs, mem_tag); } // Used to manually signal a mapper to handle a set of regions as committed. diff --git a/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp b/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp index 9550e57698e..2e509e79ec3 100644 --- a/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp +++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.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 @@ -27,7 +27,7 @@ #include "gc/g1/g1RegionsOnNodes.hpp" G1RegionsOnNodes::G1RegionsOnNodes() : _count_per_node(nullptr), _numa(G1NUMA::numa()) { - _count_per_node = NEW_C_HEAP_ARRAY(uint, _numa->num_active_nodes(), mtGC); + _count_per_node = NEW_C_HEAP_ARRAY(Atomic, _numa->num_active_nodes(), mtGC); clear(); } @@ -40,16 +40,14 @@ void G1RegionsOnNodes::add(G1HeapRegion* hr) { // Update only if the node index is valid. if (node_index < _numa->num_active_nodes()) { - *(_count_per_node + node_index) += 1; + _count_per_node[node_index].add_then_fetch(1u, memory_order_relaxed); } } void G1RegionsOnNodes::clear() { - for (uint i = 0; i < _numa->num_active_nodes(); i++) { - _count_per_node[i] = 0; - } + ::new (_count_per_node) Atomic[_numa->num_active_nodes()]{}; } uint G1RegionsOnNodes::count(uint node_index) const { - return _count_per_node[node_index]; + return _count_per_node[node_index].load_relaxed(); } diff --git a/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp index fb1f2381dba..e528a147150 100644 --- a/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp +++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp @@ -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,13 +26,14 @@ #define SHARE_VM_GC_G1_G1REGIONS_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class G1NUMA; class G1HeapRegion; // Contains per node index region count class G1RegionsOnNodes : public StackObj { - volatile uint* _count_per_node; + Atomic* _count_per_node; G1NUMA* _numa; public: diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index be18a3065e9..5f58ca2e053 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -630,6 +630,8 @@ void G1RemSet::scan_collection_set_code_roots(G1ParScanThreadState* pss, // set regions for all threads. _g1h->collection_set_iterate_increment_from(&cl, worker_id); + pss->update_nmethod_regions_to_add(); + p->record_or_add_thread_work_item(coderoots_phase, worker_id, cl.code_roots_scanned(), G1GCPhaseTimes::CodeRootsScannedNMethods); } @@ -1061,11 +1063,10 @@ class G1MergeHeapRootsTask : public WorkerTask { // so the bitmap for the regions in the collection set must be cleared if not already. if (should_clear_region(hr)) { _g1h->clear_bitmap_for_region(hr); - _g1h->concurrent_mark()->reset_top_at_mark_start(hr); } else { assert_bitmap_clear(hr, _g1h->concurrent_mark()->mark_bitmap()); } - _g1h->concurrent_mark()->clear_statistics(hr); + _g1h->concurrent_mark()->reset_region_marking_state(hr); _scan_state->add_all_dirty_region(hr->hrm_index()); return false; } @@ -1232,14 +1233,14 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { { WorkerThreads* workers = g1h->workers(); - size_t const increment_length = g1h->collection_set()->groups_increment_length(); + uint const num_groups_in_increment = g1h->collection_set()->num_groups_in_increment(); uint const num_workers = initial_evacuation ? workers->active_workers() : - MIN2(workers->active_workers(), (uint)increment_length); + MIN2(workers->active_workers(), num_groups_in_increment); G1MergeHeapRootsTask cl(_scan_state, num_workers, initial_evacuation); - log_debug(gc, ergo)("Running %s using %u workers for %zu regions", - cl.name(), num_workers, increment_length); + log_debug(gc, ergo)("Running %s using %u workers for %u groups", + cl.name(), num_workers, num_groups_in_increment); workers->run_task(&cl, num_workers); } diff --git a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp similarity index 79% rename from src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp rename to src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp index 2f7acd9b710..71c8d7bf772 100644 --- a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp +++ b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.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 @@ -24,12 +24,12 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1Policy.hpp" -#include "gc/g1/g1ReviseYoungLengthTask.hpp" +#include "gc/g1/g1ReviseNumYoungRegionsTask.hpp" #include "gc/g1/g1ServiceThread.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -jlong G1ReviseYoungLengthTask::reschedule_delay_ms() const { +jlong G1ReviseNumYoungRegionsTask::reschedule_delay_ms() const { G1Policy* policy = G1CollectedHeap::heap()->policy(); size_t available_bytes; if (policy->try_get_available_bytes_estimate(available_bytes)) { @@ -47,7 +47,7 @@ jlong G1ReviseYoungLengthTask::reschedule_delay_ms() const { } } -class G1ReviseYoungLengthTask::RemSetSamplingClosure : public G1HeapRegionClosure { +class G1ReviseNumYoungRegionsTask::RemSetSamplingClosure : public G1HeapRegionClosure { size_t _sampled_code_root_rs_length; public: @@ -62,16 +62,16 @@ public: size_t sampled_code_root_rs_length() const { return _sampled_code_root_rs_length; } }; -void G1ReviseYoungLengthTask::adjust_young_list_target_length() { +void G1ReviseNumYoungRegionsTask::adjust_target_num_young_regions() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1Policy* policy = g1h->policy(); - assert(policy->use_adaptive_young_list_length(), "should not call otherwise"); + assert(policy->use_adaptive_num_young_regions(), "should not call otherwise"); size_t pending_cards; size_t current_to_collection_set_cards; { - MutexLocker x(G1ReviseYoungLength_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1ReviseNumYoungRegions_lock, Mutex::_no_safepoint_check_flag); pending_cards = policy->current_pending_cards(); current_to_collection_set_cards = policy->current_to_collection_set_cards(); } @@ -79,18 +79,18 @@ void G1ReviseYoungLengthTask::adjust_young_list_target_length() { RemSetSamplingClosure cl; g1h->collection_set()->iterate(&cl); - policy->revise_young_list_target_length(pending_cards, + policy->revise_target_num_young_regions(pending_cards, current_to_collection_set_cards, cl.sampled_code_root_rs_length()); } -G1ReviseYoungLengthTask::G1ReviseYoungLengthTask(const char* name) : +G1ReviseNumYoungRegionsTask::G1ReviseNumYoungRegionsTask(const char* name) : G1ServiceTask(name) { } -void G1ReviseYoungLengthTask::execute() { +void G1ReviseNumYoungRegionsTask::execute() { SuspendibleThreadSetJoiner sts; - adjust_young_list_target_length(); + adjust_target_num_young_regions(); schedule(reschedule_delay_ms()); } diff --git a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp similarity index 73% rename from src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp rename to src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp index baa8af75fb7..8bc9e256366 100644 --- a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp +++ b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.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 @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP -#define SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP +#ifndef SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP +#define SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1HeapRegionRemSet.hpp" @@ -32,18 +32,18 @@ #include "utilities/growableArray.hpp" #include "utilities/ticks.hpp" -// ServiceTask to revise the young generation target length. -class G1ReviseYoungLengthTask : public G1ServiceTask { +// ServiceTask to revise the target number of young regions. +class G1ReviseNumYoungRegionsTask : public G1ServiceTask { // The delay used to reschedule this task. jlong reschedule_delay_ms() const; class RemSetSamplingClosure; // Helper class for calculating remembered set summary. - // Adjust the target length (in regions) of the young gen, based on the - // current length of the remembered sets. + // Adjust the target number of young regions, based on the + // current occupancy of the remembered sets. // - // At the end of the GC G1 determines the length of the young gen based on + // At the end of the GC G1 determines the number of young regions based on // how much time the next GC can take, and when the next GC may occur // according to the MMU. // @@ -51,13 +51,13 @@ class G1ReviseYoungLengthTask : public G1ServiceTask { // the remembered sets (and many other components), so this thread constantly // reevaluates the prediction for the remembered set scanning costs, and potentially // resizes the young gen. This may do a premature GC or even increase the young - // gen size to keep pause time length goal. - void adjust_young_list_target_length(); + // gen size to keep pause time goal. + void adjust_target_num_young_regions(); public: - explicit G1ReviseYoungLengthTask(const char* name); + explicit G1ReviseNumYoungRegionsTask(const char* name); void execute() override; }; -#endif // SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP \ No newline at end of file +#endif // SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP diff --git a/src/hotspot/share/gc/g1/g1RootClosures.hpp b/src/hotspot/share/gc/g1/g1RootClosures.hpp index 35ce038e1f8..c1c80655c91 100644 --- a/src/hotspot/share/gc/g1/g1RootClosures.hpp +++ b/src/hotspot/share/gc/g1/g1RootClosures.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 @@ -42,6 +42,8 @@ public: // Applied to nmethods reachable as strong roots. virtual NMethodClosure* strong_nmethods() = 0; + + virtual ~G1RootClosures() = default; }; class G1EvacuationRootClosures : public G1RootClosures { diff --git a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp index 7d197c37158..b913bdc2525 100644 --- a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp +++ b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.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 diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.hpp b/src/hotspot/share/gc/g1/g1ServiceThread.hpp index ff58a5f26f2..f6d8c9bf138 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.hpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.hpp @@ -32,8 +32,8 @@ class G1ServiceTaskQueue; class G1ServiceThread; class G1ServiceTask : public CHeapObj { - friend class G1ServiceTaskQueue; friend class G1ServiceThread; + friend class G1ServiceTaskQueue; // The next absolute time this task should be executed. jlong _time; diff --git a/src/hotspot/share/gc/g1/g1SharedClosures.hpp b/src/hotspot/share/gc/g1/g1SharedClosures.hpp index a81f62ff308..dc6ff646271 100644 --- a/src/hotspot/share/gc/g1/g1SharedClosures.hpp +++ b/src/hotspot/share/gc/g1/g1SharedClosures.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 @@ -55,7 +55,7 @@ public: _oops_in_cld(g1h, pss), _oops_in_nmethod(g1h, pss), _clds(&_oops_in_cld, process_only_dirty), - _nmethods(pss->worker_id(), &_oops_in_nmethod, should_mark) {} + _nmethods(pss->worker_id(), &_oops_in_nmethod, should_mark, pss) {} }; #endif // SHARE_GC_G1_G1SHAREDCLOSURES_HPP diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp index 84609df4fc9..806df4abacb 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.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 @@ -55,10 +55,10 @@ void G1SurvivorRegions::convert_to_eden() { void G1SurvivorRegions::clear() { _regions.clear(); - _used_bytes = 0; + _used_bytes.store_relaxed(0); _regions_on_node.clear(); } void G1SurvivorRegions::add_used_bytes(size_t used_bytes) { - _used_bytes += used_bytes; + _used_bytes.add_then_fetch(used_bytes, memory_order_relaxed); } diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp index 4e4966f6797..5ced2fced3c 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.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 @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1SURVIVORREGIONS_HPP #include "gc/g1/g1RegionsOnNodes.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" @@ -36,7 +37,7 @@ class G1HeapRegion; // Set of current survivor regions. class G1SurvivorRegions { GrowableArray _regions; - volatile size_t _used_bytes; + Atomic _used_bytes; G1RegionsOnNodes _regions_on_node; public: @@ -56,7 +57,7 @@ public: } // Used bytes of all survivor regions. - size_t used_bytes() const { return _used_bytes; } + size_t used_bytes() const { return _used_bytes.load_relaxed(); } void add_used_bytes(size_t used_bytes); }; diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 891432e20a7..577f3b5491d 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -22,6 +22,7 @@ * */ +#include "code/codeCache.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" @@ -66,7 +67,7 @@ VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(size_t allocation_word_size, _transient_failure(false), _mark_in_progress(false), _cycle_already_in_progress(false), - _whitebox_attached(false), + _whitebox_controlled(false), _gc_succeeded(false) {} @@ -88,19 +89,26 @@ void VM_G1TryInitiateConcMark::doit() { G1CollectorState* state = g1h->collector_state(); _mark_in_progress = state->is_in_marking(); _cycle_already_in_progress = state->is_in_concurrent_cycle(); + _whitebox_controlled = (_gc_cause != GCCause::_wb_breakpoint) && ConcurrentGCBreakpoints::is_controlled(); - if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { + // Notify the code cache that we deferred clearing the unloading GC request if we are WhiteBox controlled + // and we are going to suppress it. If marking is active, we do not need to suppress because that will satisfy the + // request already. + // This needs to be atomic wrt. to all code-cache allocation threads to allow setting the request + // after WhiteBox releases control again. + bool defer_codecache_request = whitebox_controlled() && + GCCause::is_codecache_requested_gc(_gc_cause) && + !mark_in_progress(); + if (defer_codecache_request) { + CodeCache::defer_unloading_gc_request(); + return; + } else if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { // Failure to force the next GC pause to be a concurrent start indicates // there is already a concurrent marking cycle in progress. Flags to indicate // that were already set, so return immediately. - } else if ((_gc_cause != GCCause::_wb_breakpoint) && - ConcurrentGCBreakpoints::is_controlled()) { - // WhiteBox wants to be in control of concurrent cycles, so don't try to - // start one. This check is after the force_concurrent_start_xxx so that a - // request will be remembered for a later partial collection, even though - // we've rejected this request. - _whitebox_attached = true; - } else { + return; + } else if (!whitebox_controlled()) { + // Only run a concurrent marking if not controlled by WhiteBox. g1h->do_collection_pause_at_safepoint(_word_size); _gc_succeeded = true; } @@ -130,8 +138,13 @@ void VM_G1CollectForAllocation::doit() { } void VM_G1PauseConcurrent::doit() { - GCIdMark gc_id_mark(_gc_id); G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (_is_shutting_down) { + g1h->concurrent_mark()->shutdown_concurrent_cycle(); + return; + } + + GCIdMark gc_id_mark(_gc_id); GCTraceCPUTime tcpu(g1h->concurrent_mark()->gc_tracer_cm()); // GCTraceTime(...) only supports sub-phases, so a more verbose version @@ -140,7 +153,7 @@ void VM_G1PauseConcurrent::doit() { GCTraceTimePauseTimer timer(_message, g1h->concurrent_mark()->gc_timer_cm()); GCTraceTimeDriver t(&logger, &timer); - G1ConcGCMonitoringScope monitoring_scope(g1h->monitoring_support()); + G1ConcGCMonitoringScope monitoring_scope(g1h->monitoring_support(), affects_memory_pools()); SvcGCMarker sgcm(SvcGCMarker::CONCURRENT); IsSTWGCActiveMark x; @@ -150,12 +163,9 @@ void VM_G1PauseConcurrent::doit() { bool VM_G1PauseConcurrent::doit_prologue() { Heap_lock->lock(); G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_shutting_down()) { + _is_shutting_down = g1h->is_shutting_down(); + if (_is_shutting_down && !g1h->concurrent_mark()->shutdown_cleanup_needed()) { Heap_lock->unlock(); - // 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; } return true; @@ -177,3 +187,19 @@ void VM_G1PauseCleanup::work() { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); cm->cleanup(); } + +bool VM_G1StopMarking::doit_prologue() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); +#ifdef ASSERT + { + MutexLocker ml(Heap_lock); + assert(g1h->is_shutting_down(), "must be"); + } +#endif + return g1h->concurrent_mark()->shutdown_cleanup_needed(); +} + +void VM_G1StopMarking::doit() { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->shutdown_concurrent_cycle(); +} diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp index 5429051496a..0c12e75eef0 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.hpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp @@ -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 @@ -48,7 +48,7 @@ class VM_G1TryInitiateConcMark : public VM_GC_Collect_Operation { bool _transient_failure; bool _mark_in_progress; bool _cycle_already_in_progress; - bool _whitebox_attached; + bool _whitebox_controlled; // The concurrent start pause may be cancelled for some reasons. Keep track of // this. bool _gc_succeeded; @@ -63,7 +63,7 @@ public: bool transient_failure() const { return _transient_failure; } bool mark_in_progress() const { return _mark_in_progress; } bool cycle_already_in_progress() const { return _cycle_already_in_progress; } - bool whitebox_attached() const { return _whitebox_attached; } + bool whitebox_controlled() const { return _whitebox_controlled; } bool gc_succeeded() const { return _gc_succeeded && VM_GC_Operation::gc_succeeded(); } }; @@ -80,13 +80,18 @@ public: // Concurrent G1 stop-the-world operations such as remark and cleanup. class VM_G1PauseConcurrent : public VM_Operation { uint _gc_id; + bool _is_shutting_down; const char* _message; protected: VM_G1PauseConcurrent(const char* message) : - _gc_id(GCId::current()), _message(message) { } + _gc_id(GCId::current()), _is_shutting_down(false), _message(message) { } virtual void work() = 0; + // Does this concurrent pause affect the memory pools? If so, update the collectionUsage() + // MemoryMXBean for the old gen memory pool (which is the only pool registered for concurrent + // pauses). + virtual bool affects_memory_pools() const = 0; public: bool doit_prologue() override; void doit_epilogue() override; @@ -95,6 +100,8 @@ public: }; class VM_G1PauseRemark : public VM_G1PauseConcurrent { + bool affects_memory_pools() const override { return true; } + public: VM_G1PauseRemark() : VM_G1PauseConcurrent("Pause Remark") { } VMOp_Type type() const override { return VMOp_G1PauseRemark; } @@ -102,10 +109,23 @@ public: }; class VM_G1PauseCleanup : public VM_G1PauseConcurrent { + bool affects_memory_pools() const override { return false; } + public: VM_G1PauseCleanup() : VM_G1PauseConcurrent("Pause Cleanup") { } VMOp_Type type() const override { return VMOp_G1PauseCleanup; } void work() override; }; +class VM_G1StopMarking : public VM_Operation { +public: + VM_G1StopMarking() : VM_Operation() { } + VMOp_Type type() const override { return VMOp_G1StopMarking; } + + bool doit_prologue() override; + void doit() override; + + bool is_gc_operation() const override { return true; } +}; + #endif // SHARE_GC_G1_G1VMOPERATIONS_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 810b54ec587..ec83d8a27d3 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -260,7 +260,7 @@ void G1YoungCollector::calculate_collection_set(G1EvacInfo* evacuation_info, dou allocator()->release_mutator_alloc_regions(); collection_set()->finalize_initial_collection_set(target_pause_time_ms, survivor_regions()); - evacuation_info->set_collection_set_regions(collection_set()->initial_region_length() + + evacuation_info->set_collection_set_regions(collection_set()->num_initial_regions() + collection_set()->num_optional_regions()); concurrent_mark()->verify_no_collection_set_oops(); @@ -362,13 +362,14 @@ class G1PrepareEvacuationTask : public WorkerTask { // There is no difference between scanning cards covering an effectively // dead humongous object vs. some other objects in reallocated regions. // - // TAMSes are only reset after completing the entire mark cycle, during - // bitmap clearing. It is worth to not wait until then, and allow reclamation - // outside of actual (concurrent) SATB marking. + // TAMSes are only reset in the Concurrent Start pause and when they are + // reclaimed/freed. It is worth to not wait for TAMS updates until either + // of these conditions applies and allow reclamation as much as possible. // This also applies to the concurrent start pause - we only set - // mark_in_progress() at the end of that GC: no mutator is running that can + // is_in_marking() at the end of that GC: no mutator is running that can // sneakily install a new reference to the potentially reclaimed humongous // object. + // // During the concurrent start pause the situation described above where we // miss a reference can not happen. No mutator is modifying the object // graph to install such an overlooked reference. @@ -376,12 +377,15 @@ class G1PrepareEvacuationTask : public WorkerTask { // After the pause, having reclaimed h, obviously the mutator can't fetch // the reference from h any more. if (!obj->is_typeArray()) { - // All regions that were allocated before marking have a TAMS != bottom. - bool allocated_before_mark_start = region->bottom() != _g1h->concurrent_mark()->top_at_mark_start(region); bool mark_in_progress = _g1h->collector_state()->is_in_marking(); - - if (allocated_before_mark_start && mark_in_progress) { - return false; + // top_at_mark_start() will assert outside of marking, so check first. + if (mark_in_progress) { + // All regions that were allocated before marking have a TAMS != bottom. + G1ConcurrentMark* cm = _g1h->concurrent_mark(); + bool allocated_before_mark_start = region->bottom() != cm->top_at_mark_start(region); + if (allocated_before_mark_start) { + return false; + } } } return _g1h->is_potential_eager_reclaim_candidate(region); @@ -1028,7 +1032,7 @@ void G1YoungCollector::enqueue_candidates_as_root_regions() { G1CollectionSetCandidates* candidates = collection_set()->candidates(); candidates->iterate_regions([&] (G1HeapRegion* r) { - _g1h->concurrent_mark()->add_root_region(r); + _g1h->concurrent_mark()->add_root_region_set_bottom(r); }); } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index 7415bc83827..e9f2477ea76 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -144,7 +144,7 @@ public: size_t allocation_word_size); void collect(); - G1CollectorState next_state() const { return _next_state; } + const G1CollectorState next_state() const { return _next_state; } bool concurrent_operation_is_full_mark() const { return _concurrent_operation_is_full_mark; } }; diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 2b33a85da29..0d922c0a3ed 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -56,7 +56,7 @@ G1YoungGCAllocationFailureInjector::G1YoungGCAllocationFailureInjector() void G1YoungGCAllocationFailureInjector::select_allocation_failure_regions() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); _allocation_failure_regions.reinitialize(g1h->max_num_regions()); - SelectAllocationFailureRegionClosure closure(_allocation_failure_regions, g1h->collection_set()->cur_length()); + SelectAllocationFailureRegionClosure closure(_allocation_failure_regions, g1h->collection_set()->num_regions()); g1h->collection_set_iterate_all(&closure); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index bf4a6cca81d..e561252ab25 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -54,12 +54,12 @@ #include "utilities/bitMap.inline.hpp" #include "utilities/ticks.hpp" -class G1PostEvacuateCollectionSetCleanupTask1::MergePssTask : public G1AbstractSubTask { +class G1PostEvacuateCollectionSetCleanupTask1::FlushPssTask : public G1AbstractSubTask { G1ParScanThreadStateSet* _per_thread_states; public: - MergePssTask(G1ParScanThreadStateSet* per_thread_states) : - G1AbstractSubTask(G1GCPhaseTimes::MergePSS), + FlushPssTask(G1ParScanThreadStateSet* per_thread_states) : + G1AbstractSubTask(G1GCPhaseTimes::FlushPSS), _per_thread_states(per_thread_states) { } double worker_cost() const override { return 1.0; } @@ -119,6 +119,58 @@ public: } }; +class G1PostEvacuateCollectionSetCleanupTask1::UpdateCodeRootsTask + : public G1AbstractSubTask +{ + class ProcessRegionClosure : public G1HeapRegionClosure { + G1ParScanThreadStateSet* _psss; + + public: + ProcessRegionClosure(G1ParScanThreadStateSet* psss) : _psss(psss) { } + + bool do_heap_region(G1HeapRegion* r) override { + uint index = r->hrm_index(); + + size_t num_nmethods = 0; + for (uint i = 0; i < _psss->num_workers(); i++) { + G1ParScanThreadState* pss = _psss->state_for_worker(i); + num_nmethods += pss->num_nmethods(index); + } + if (num_nmethods != 0) { + // Notify the code root sets that we are going to add code roots. + r->rem_set()->prepare_for_adding_code_roots(num_nmethods); + + // Add roots. + for (uint i = 0; i < _psss->num_workers(); i++) { + G1ParScanThreadState* pss = _psss->state_for_worker(i); + pss->iterate_nmethods(index, [&] (nmethod* nm) { r->add_code_root(nm); }); + } + } + return false; + } + }; + + G1ParScanThreadStateSet* _psss; + G1HeapRegionClaimer _claimer; + +public: + UpdateCodeRootsTask(G1ParScanThreadStateSet* per_thread_states) + : G1AbstractSubTask(G1GCPhaseTimes::UpdateCodeRoots), _psss(per_thread_states), _claimer(0) { } + + double worker_cost() const override { + return _psss->num_nmethod_regions_to_add(); + } + + void set_max_workers(uint max_workers) override { + _claimer.set_n_workers(max_workers); + } + + void do_work(uint worker_id) override { + ProcessRegionClosure cl(_psss); + _psss->par_iterate_nmethod_regions_to_add(&cl, &_claimer, worker_id); + } +}; + class G1PostEvacuateCollectionSetCleanupTask1::RestoreEvacFailureRegionsTask : public G1AbstractSubTask { G1CollectedHeap* _g1h; G1ConcurrentMark* _cm; @@ -327,11 +379,13 @@ G1PostEvacuateCollectionSetCleanupTask1::G1PostEvacuateCollectionSetCleanupTask1 bool evac_failed = evac_failure_regions->has_regions_evac_failed(); bool alloc_failed = evac_failure_regions->has_regions_alloc_failed(); - add_serial_task(new MergePssTask(per_thread_states)); + add_serial_task(new FlushPssTask(per_thread_states)); add_serial_task(new RecalculateUsedTask(evac_failed, alloc_failed)); if (SampleCollectionSetCandidatesTask::should_execute()) { add_serial_task(new SampleCollectionSetCandidatesTask()); } + add_parallel_task(new UpdateCodeRootsTask(per_thread_states)); + add_parallel_task(G1CollectedHeap::heap()->rem_set()->create_cleanup_after_scan_heap_roots_task()); if (evac_failed) { add_parallel_task(new RestoreEvacFailureRegionsTask(evac_failure_regions)); @@ -394,8 +448,13 @@ public: oop obj = cast_to_oop(r->bottom()); { ResourceMark rm; - bool allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); bool mark_in_progress = _g1h->collector_state()->is_in_marking(); + bool allocated_after_mark_start = false; + if (mark_in_progress) { + // top_at_mark_start() will assert if we are not in marking, so check first. + allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); + } + guarantee(obj->is_typeArray() || (allocated_after_mark_start || !mark_in_progress), "Only eagerly reclaiming primitive arrays is supported, other humongous objects only if allocated after mark start, but the object " PTR_FORMAT " (%s) is not (mark %d allocated after mark: %d).", @@ -498,20 +557,22 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1ConcurrentMark* cm = g1h->concurrent_mark(); - // Concurrent mark does not mark through regions that we retain (they are root - // regions wrt to marking), so we must clear their mark data (tams, bitmap, ...) - // set eagerly or during evacuation failure. + // Retained regions are root regions for marking, so we must clear their mark data + // (tams, bitmap, ...). Outside of Concurrent Start GC we must always clear the mark data + // for the next GC. bool clear_mark_data = !g1h->collector_state()->is_in_concurrent_start_gc() || g1h->policy()->should_retain_evac_failed_region(r); if (clear_mark_data) { g1h->clear_bitmap_for_region(r); + // Must be because this is a region that should not have been selected to + // be marked through. + cm->assert_top_at_mark_start_is_bottom(r); } else { // This evacuation failed region is going to be marked through. Update mark data. - cm->update_top_at_mark_start(r); - cm->set_live_bytes(r->hrm_index(), r->live_bytes()); - assert(cm->mark_bitmap()->get_next_marked_addr(r->bottom(), cm->top_at_mark_start(r)) != cm->top_at_mark_start(r), - "Marks must be on bitmap for region %u", r->hrm_index()); + // Since we have some marked live data information, pass that too. + cm->assert_statistics_clear(r); + cm->notify_new_region(r, r->live_bytes()); } return false; } @@ -649,9 +710,9 @@ class FreeCSetClosure : public G1HeapRegionClosure { void assert_tracks_surviving_words(G1HeapRegion* r) { assert(r->young_index_in_cset() != 0 && - (uint)r->young_index_in_cset() <= _g1h->collection_set()->young_region_length(), + (uint)r->young_index_in_cset() <= _g1h->collection_set()->num_young_regions(), "Young index %u is wrong for region %u of type %s with %u young regions", - r->young_index_in_cset(), r->hrm_index(), r->get_type_str(), _g1h->collection_set()->young_region_length()); + r->young_index_in_cset(), r->hrm_index(), r->get_type_str(), _g1h->collection_set()->num_young_regions()); } void handle_evacuated_region(G1HeapRegion* r) { @@ -810,7 +871,7 @@ public: p->record_serial_free_cset_time_ms((Ticks::now() - serial_time).seconds() * 1000.0); } - double worker_cost() const override { return G1CollectedHeap::heap()->collection_set()->initial_region_length(); } + double worker_cost() const override { return G1CollectedHeap::heap()->collection_set()->num_initial_regions(); } void set_max_workers(uint max_workers) override { _active_workers = max_workers; @@ -864,21 +925,19 @@ public: } }; -class G1PostEvacuateCollectionSetCleanupTask2::ResetPartialArrayStateManagerTask - : public G1AbstractSubTask -{ -public: - ResetPartialArrayStateManagerTask() - : G1AbstractSubTask(G1GCPhaseTimes::ResetPartialArrayStateManager) - {} +class G1PostEvacuateCollectionSetCleanupTask2::DestroyPssTask : public G1AbstractSubTask { + G1ParScanThreadStateSet* _per_thread_states; - double worker_cost() const override { - return AlmostNoWork; - } +public: + DestroyPssTask(G1ParScanThreadStateSet* per_thread_states) : + G1AbstractSubTask(G1GCPhaseTimes::DestroyPSS), + _per_thread_states(per_thread_states) { } + + double worker_cost() const override { return 1.0; } void do_work(uint worker_id) override { - // This must be in phase2 cleanup, after phase1 has destroyed all of the - // associated allocators. + _per_thread_states->destroy_worker_states(); + // This must be here after above destroyed the per-thread allocators. G1CollectedHeap::heap()->partial_array_state_manager()->reset(); } }; @@ -894,7 +953,7 @@ G1PostEvacuateCollectionSetCleanupTask2::G1PostEvacuateCollectionSetCleanupTask2 if (G1CollectedHeap::heap()->has_humongous_reclaim_candidates()) { add_serial_task(new EagerlyReclaimHumongousObjectsTask()); } - add_serial_task(new ResetPartialArrayStateManagerTask()); + add_serial_task(new DestroyPssTask(per_thread_states)); if (evac_failure_regions->has_regions_evac_failed()) { add_parallel_task(new ProcessEvacuationFailedRegionsTask(evac_failure_regions)); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp index 557ce454c78..95d0fee6ad7 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp @@ -35,16 +35,18 @@ class G1EvacInfo; class G1ParScanThreadStateSet; // First set of post evacuate collection set tasks containing ("s" means serial): -// - Merge PSS (s) +// - Flush PSS (s) // - Recalculate Used (s) // - Sample Collection Set Candidates (s) // - Clear Card Table // - Restore evac failure regions (on evacuation failure) +// - Update code roots (for regions that need code roots to be added) class G1PostEvacuateCollectionSetCleanupTask1 : public G1BatchedTask { - class MergePssTask; + class FlushPssTask; class RecalculateUsedTask; class SampleCollectionSetCandidatesTask; class RestoreEvacFailureRegionsTask; + class UpdateCodeRootsTask; public: G1PostEvacuateCollectionSetCleanupTask1(G1ParScanThreadStateSet* per_thread_states, @@ -54,10 +56,10 @@ public: // Second set of post evacuate collection set tasks containing (s means serial): // - Eagerly Reclaim Humongous Objects (s) // - Update Derived Pointers (s) +// - Destroy PSS (s) + Reset the reusable PartialArrayStateManager // - Clear Retained Region Data (on evacuation failure) // - Free Collection Set // - Resize TLABs and Swap Card Table -// - Reset the reusable PartialArrayStateManager. class G1PostEvacuateCollectionSetCleanupTask2 : public G1BatchedTask { class EagerlyReclaimHumongousObjectsTask; #ifdef COMPILER2 @@ -67,7 +69,8 @@ class G1PostEvacuateCollectionSetCleanupTask2 : public G1BatchedTask { class ProcessEvacuationFailedRegionsTask; class FreeCollectionSetTask; class ResizeTLABsAndSwapCardTableTask; - class ResetPartialArrayStateManagerTask; + + class DestroyPssTask; public: G1PostEvacuateCollectionSetCleanupTask2(G1ParScanThreadStateSet* per_thread_states, diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp index 60c79ec28df..e817ebcab56 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp @@ -30,7 +30,7 @@ #include "runtime/globals_extension.hpp" G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), - _use_adaptive_sizing(true), _min_desired_young_length(), _max_desired_young_length(0) { + _use_adaptive_sizing(true), _min_desired_num_regions(), _max_desired_num_regions(0) { precond(!FLAG_IS_ERGO(NewRatio)); precond(!FLAG_IS_ERGO(NewSize)); @@ -100,16 +100,16 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), } if (user_specified_NewSize) { - _min_desired_young_length.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U)); + _min_desired_num_regions.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U)); } if (user_specified_MaxNewSize) { - _max_desired_young_length.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U)); + _max_desired_num_regions.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U)); } if (user_specified_NewSize && user_specified_MaxNewSize) { _sizer_kind = SizerMaxAndNewSize; - _use_adaptive_sizing = min_desired_young_length() != max_desired_young_length(); + _use_adaptive_sizing = min_desired_num_regions() != max_desired_num_regions(); } else if (user_specified_NewSize) { _sizer_kind = SizerNewSizeOnly; } else { @@ -118,52 +118,52 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), } } -uint G1YoungGenSizer::calculate_default_min_length(uint new_number_of_heap_regions) { +uint G1YoungGenSizer::calculate_default_min_num_regions(uint new_number_of_heap_regions) { uint default_value = (new_number_of_heap_regions * G1NewSizePercent) / 100; return MAX2(1U, default_value); } -uint G1YoungGenSizer::calculate_default_max_length(uint new_number_of_heap_regions) { +uint G1YoungGenSizer::calculate_default_max_num_regions(uint new_number_of_heap_regions) { uint default_value = (new_number_of_heap_regions * G1MaxNewSizePercent) / 100; return MAX2(1U, default_value); } -void G1YoungGenSizer::recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length) { +void G1YoungGenSizer::recalculate_min_max_num_regions(uint number_of_heap_regions, uint* min_num_young_regions, uint* max_num_young_regions) { assert(number_of_heap_regions > 0, "Heap must be initialized"); switch (_sizer_kind) { case SizerDefaults: - *min_young_length = calculate_default_min_length(number_of_heap_regions); - *max_young_length = calculate_default_max_length(number_of_heap_regions); + *min_num_young_regions = calculate_default_min_num_regions(number_of_heap_regions); + *max_num_young_regions = calculate_default_max_num_regions(number_of_heap_regions); break; case SizerNewSizeOnly: - *max_young_length = calculate_default_max_length(number_of_heap_regions); - *max_young_length = MAX2(*min_young_length, *max_young_length); + *max_num_young_regions = calculate_default_max_num_regions(number_of_heap_regions); + *max_num_young_regions = MAX2(*min_num_young_regions, *max_num_young_regions); break; case SizerMaxNewSizeOnly: - *min_young_length = calculate_default_min_length(number_of_heap_regions); - *min_young_length = MIN2(*min_young_length, *max_young_length); + *min_num_young_regions = calculate_default_min_num_regions(number_of_heap_regions); + *min_num_young_regions = MIN2(*min_num_young_regions, *max_num_young_regions); break; case SizerMaxAndNewSize: // Do nothing. Values set on the command line, don't update them at runtime. break; case SizerNewRatio: - *min_young_length = MAX2((uint)(number_of_heap_regions / (NewRatio + 1)), 1u); - *max_young_length = *min_young_length; + *min_num_young_regions = MAX2((uint)(number_of_heap_regions / (NewRatio + 1)), 1u); + *max_num_young_regions = *min_num_young_regions; break; default: ShouldNotReachHere(); } - assert(*min_young_length <= *max_young_length, "Invalid min/max young gen size values"); + assert(*min_num_young_regions <= *max_num_young_regions, "Invalid min/max young gen size values"); } void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { // We need to pass the desired values because recalculation may not update these // values in some cases. - uint unused_new_min = min_desired_young_length(); - uint new_max = max_desired_young_length(); - recalculate_min_max_young_length(number_of_heap_regions, &unused_new_min, &new_max); + uint unused_new_min = min_desired_num_regions(); + uint new_max = max_desired_num_regions(); + recalculate_min_max_num_regions(number_of_heap_regions, &unused_new_min, &new_max); size_t max_young_size = new_max * G1HeapRegion::GrainBytes; if (max_young_size != MaxNewSize) { @@ -172,9 +172,9 @@ void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { } void G1YoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) { - uint min = min_desired_young_length(); - uint max = max_desired_young_length(); - recalculate_min_max_young_length(new_number_of_heap_regions, &min, &max); - _min_desired_young_length.store_relaxed(min); - _max_desired_young_length.store_relaxed(max); + uint min = min_desired_num_regions(); + uint max = max_desired_num_regions(); + recalculate_min_max_num_regions(new_number_of_heap_regions, &min, &max); + _min_desired_num_regions.store_relaxed(min); + _max_desired_num_regions.store_relaxed(max); } diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp index c60c3c373a9..ea269f72559 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp @@ -79,31 +79,31 @@ private: // true otherwise. bool _use_adaptive_sizing; - Atomic _min_desired_young_length; - Atomic _max_desired_young_length; + Atomic _min_desired_num_regions; + Atomic _max_desired_num_regions; - uint calculate_default_min_length(uint new_number_of_heap_regions); - uint calculate_default_max_length(uint new_number_of_heap_regions); + uint calculate_default_min_num_regions(uint new_number_of_heap_regions); + uint calculate_default_max_num_regions(uint new_number_of_heap_regions); - // Update the given values for minimum and maximum young gen length in regions - // given the number of heap regions depending on the kind of sizing algorithm. - void recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length); + // Recalculate the minimum and maximum number of young regions for the + // given number of heap regions according to the current sizing algorithm. + void recalculate_min_max_num_regions(uint number_of_heap_regions, uint* min_num_young_regions, uint* max_num_young_regions); public: G1YoungGenSizer(); - // Calculate the maximum length of the young gen given the number of regions + // Calculate the maximum size of the young gen given the number of regions // depending on the sizing algorithm. virtual void adjust_max_new_size(uint number_of_heap_regions); virtual void heap_size_changed(uint new_number_of_heap_regions); - uint min_desired_young_length() const { - return _min_desired_young_length.load_relaxed(); + uint min_desired_num_regions() const { + return _min_desired_num_regions.load_relaxed(); } - uint max_desired_young_length() const { - return _max_desired_young_length.load_relaxed(); + uint max_desired_num_regions() const { + return _max_desired_num_regions.load_relaxed(); } - bool use_adaptive_young_list_length() const { + bool use_adaptive_num_young_regions() const { return _use_adaptive_sizing; } }; diff --git a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp index df6adeb8041..2ad3500de4a 100644 --- a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp +++ b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.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 @@ -165,7 +165,7 @@ JVMFlag::Error GCPauseIntervalMillisConstraintFuncG1(uintx value, bool verbose) JVMFlag::Error NewSizeConstraintFuncG1(size_t value, bool verbose) { #ifdef _LP64 - // Overflow would happen for uint type variable of YoungGenSizer::_min_desired_young_length + // Overflow would happen for uint type variable of YoungGenSizer::_min_desired_num_regions // when the value to be assigned exceeds uint range. // i.e. result of '(uint)(NewSize / region size(1~32MB))' // So maximum of NewSize should be 'max_juint * 1M' diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index af236ec8581..23beb75211b 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.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. * 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,9 +42,9 @@ nonstatic_field(G1HeapRegion, _bottom, HeapWord* const) \ nonstatic_field(G1HeapRegion, _top, Atomic) \ nonstatic_field(G1HeapRegion, _end, HeapWord* const) \ - volatile_nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic)\ + nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic) \ \ - nonstatic_field(G1HeapRegionType, _tag, G1HeapRegionType::Tag volatile) \ + nonstatic_field(G1HeapRegionType, _tag, Atomic) \ \ \ nonstatic_field(G1HeapRegionTable, _base, address) \ @@ -55,7 +55,7 @@ \ nonstatic_field(G1HeapRegionManager, _regions, G1HeapRegionTable) \ \ - volatile_nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ + nonstatic_field(G1CollectedHeap, _summary_bytes_used, Atomic) \ nonstatic_field(G1CollectedHeap, _hrm, G1HeapRegionManager) \ nonstatic_field(G1CollectedHeap, _monitoring_support, G1MonitoringSupport*) \ nonstatic_field(G1CollectedHeap, _old_set, G1HeapRegionSetBase) \ @@ -104,6 +104,6 @@ declare_toplevel_type(G1HeapRegion*) \ declare_toplevel_type(G1MonitoringSupport*) \ \ - declare_integer_type(G1HeapRegionType::Tag volatile) + declare_integer_type(Atomic) #endif // SHARE_GC_G1_VMSTRUCTS_G1_HPP diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 8b514fe7199..ca4e77bab8e 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -154,7 +154,7 @@ void MutableNUMASpace::bias_region(MemRegion mr, uint lgrp_id) { // First we tell the OS which page size we want in the given range. The underlying // large page can be broken down if we require small pages. os::realign_memory((char*) mr.start(), mr.byte_size(), page_size()); - // Then we uncommit the pages in the range. + // Then we disclaim the pages in the range so they can be faulted in again. os::disclaim_memory((char*) mr.start(), mr.byte_size()); // And make them local/first-touch biased. os::numa_make_local((char*)mr.start(), mr.byte_size(), checked_cast(lgrp_id)); diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index d99db493989..9b10f9faee2 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -49,7 +49,7 @@ void MutableSpace::numa_setup_pages(MemRegion mr, bool clear_space) { } if (clear_space) { - // Prefer page reallocation to migration. + // Prefer page discard and refault under the requested NUMA policy to migration. os::disclaim_memory((char*) mr.start(), mr.byte_size()); } os::numa_make_global((char*) mr.start(), mr.byte_size()); diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index b77294a2ac1..ea3a85861b8 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -308,11 +308,25 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { for (uint loop_count = 0; /* empty */; ++loop_count) { HeapWord* result; { + // This lock is needed to sync with the VM-init expansion below. ConditionalMutexLocker locker(Heap_lock, !is_init_completed()); result = mem_allocate_cas_noexpand(size, is_tlab); if (result != nullptr) { return result; } + + // Ensure that is_init_completed() does not transition while expanding the heap. + ConditionalMutexLocker ml_init(InitCompleted_lock, !is_init_completed(), Mutex::_no_safepoint_check_flag); + if (!is_init_completed()) { + // Rechecked !is_init_completed() implies we have mutual exclusion via + // `Heap_lock` and `InitCompleted_lock` + result = expand_heap_and_allocate(size, is_tlab); + // Return the result if it's tlab-allocation. If the result is null, + // callers will retry non-tlab allocation. + if (result != nullptr || is_tlab) { + return result; + } + } } // Read total_collections() under the lock so that multiple @@ -328,19 +342,6 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { return result; } - if (!is_init_completed()) { - // Double checked locking, this ensure that is_init_completed() does not - // transition while expanding the heap. - MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); - if (!is_init_completed()) { - // Can't do GC; try heap expansion to satisfy the request. - result = expand_heap_and_allocate(size, is_tlab); - if (result != nullptr) { - return result; - } - } - } - gc_count = total_collections(); } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index ee8ab3f7df0..cd56bf1c91e 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -60,7 +60,6 @@ public: class ParCompactionManager : public CHeapObj { friend class MarkFromRootsTask; friend class ParallelCompactRefProcProxyTask; - friend class ParallelScavengeRefProcProxyTask; friend class ParMarkBitMap; friend class PSParallelCompact; friend class FillDensePrefixAndCompactionTask; diff --git a/src/hotspot/share/gc/parallel/psMemoryPool.cpp b/src/hotspot/share/gc/parallel/psMemoryPool.cpp index 40170f62f06..13304ca18ad 100644 --- a/src/hotspot/share/gc/parallel/psMemoryPool.cpp +++ b/src/hotspot/share/gc/parallel/psMemoryPool.cpp @@ -32,7 +32,7 @@ PSOldGenerationPool::PSOldGenerationPool(PSOldGen* old_gen, } MemoryUsage PSOldGenerationPool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); size_t used = used_in_bytes(); size_t committed = _old_gen->capacity_in_bytes(); @@ -59,7 +59,7 @@ PSEdenSpacePool::PSEdenSpacePool(PSYoungGen* young_gen, } MemoryUsage PSEdenSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); size_t used = used_in_bytes(); size_t committed = _space->capacity_in_bytes(); @@ -80,7 +80,7 @@ PSSurvivorSpacePool::PSSurvivorSpacePool(PSYoungGen* young_gen, } MemoryUsage PSSurvivorSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); size_t used = used_in_bytes(); size_t committed = committed_in_bytes(); return MemoryUsage(initial_size(), used, committed, maxSize); diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index ff757f205a2..777b734c59e 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -644,7 +644,6 @@ void PSParallelCompact::post_compact() GCTraceTime(Info, gc, phases) tm("Post Compact", &_gc_timer); ParCompactionManager::remove_all_shadow_regions(); - CodeCache::on_gc_marking_cycle_finish(); CodeCache::arm_all_nmethods(); // Need to clear claim bits for the next full-gc (marking and adjust-pointers). @@ -1216,6 +1215,7 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { // This is the point where the entire marking should have completed. ParCompactionManager::verify_all_marking_stack_empty(); + CodeCache::on_gc_marking_cycle_finish(); { GCTraceTime(Debug, gc, phases) tm("Weak Processing", &_gc_timer); diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.hpp index edce4861d4d..287808429d3 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp @@ -55,6 +55,7 @@ class ParCompactionManager; class PSPromotionManager { friend class PSScavenge; + friend class ParallelScavengeRefProcProxyTask; friend class ScavengeRootsTask; private: diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 8dbd2485e76..883bcb81a50 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -193,7 +193,7 @@ class ParallelScavengeRefProcProxyTask : public RefProcProxyTask { public: ParallelScavengeRefProcProxyTask(uint max_workers) : RefProcProxyTask("ParallelScavengeRefProcProxyTask", max_workers), - _terminator(max_workers, ParCompactionManager::marking_stacks()) {} + _terminator(max_workers, PSPromotionManager::vm_thread_promotion_manager()->stack_array_depth()) {} void work(uint worker_id) override { assert(worker_id < _max_workers, "sanity"); diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index 13532dea07d..a88a0878305 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -512,6 +512,7 @@ void SerialFullGC::phase1_mark(bool clear_all_softrefs) { // This is the point where the entire marking should have completed. assert(_marking_stack.is_empty(), "Marking should have completed"); + CodeCache::on_gc_marking_cycle_finish(); { GCTraceTime(Debug, gc, phases) tm_m("Weak Processing", gc_timer()); diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 5d068ff67e0..eb2bed109b5 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -306,11 +306,25 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { for (uint try_count = 1; /* break */; try_count++) { { + // This lock is needed to sync with the VM-init expansion below. ConditionalMutexLocker locker(Heap_lock, !is_init_completed()); result = mem_allocate_cas_noexpand(size, is_tlab); if (result != nullptr) { break; } + + // Ensure that is_init_completed() does not transition while expanding the heap. + ConditionalMutexLocker ml_init(InitCompleted_lock, !is_init_completed(), Mutex::_no_safepoint_check_flag); + if (!is_init_completed()) { + // Rechecked !is_init_completed() implies we have mutual exclusion via + // `Heap_lock` and `InitCompleted_lock` + result = expand_heap_and_allocate(size, is_tlab); + // Return the result if it's tlab-allocation. If the result is null, + // callers will retry non-tlab allocation. + if (result != nullptr || is_tlab) { + return result; + } + } } uint gc_count_before; // Read inside the Heap_lock locked region. { @@ -323,19 +337,6 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { break; } - if (!is_init_completed()) { - // Double checked locking, this ensure that is_init_completed() does not - // transition while expanding the heap. - MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); - if (!is_init_completed()) { - // Can't do GC; try heap expansion to satisfy the request. - result = expand_heap_and_allocate(size, is_tlab); - if (result != nullptr) { - return result; - } - } - } - gc_count_before = total_collections(); } @@ -588,7 +589,6 @@ void SerialHeap::do_full_collection(bool clear_all_soft_refs) { gc_timer->register_gc_end(); gc_tracer->report_gc_end(gc_timer->gc_end(), gc_timer->time_partitions()); - CodeCache::on_gc_marking_cycle_finish(); CodeCache::arm_all_nmethods(); COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); diff --git a/src/hotspot/share/gc/serial/serialMemoryPools.cpp b/src/hotspot/share/gc/serial/serialMemoryPools.cpp index 723b53a3e2b..8b0230264ef 100644 --- a/src/hotspot/share/gc/serial/serialMemoryPools.cpp +++ b/src/hotspot/share/gc/serial/serialMemoryPools.cpp @@ -40,7 +40,7 @@ size_t ContiguousSpacePool::used_in_bytes() { } MemoryUsage ContiguousSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); size_t used = used_in_bytes(); size_t committed = _space->capacity(); @@ -64,7 +64,7 @@ size_t SurvivorContiguousSpacePool::committed_in_bytes() { } MemoryUsage SurvivorContiguousSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); size_t used = used_in_bytes(); size_t committed = committed_in_bytes(); @@ -85,7 +85,7 @@ size_t TenuredGenerationPool::used_in_bytes() { MemoryUsage TenuredGenerationPool::get_memory_usage() { size_t used = used_in_bytes(); size_t committed = _gen->capacity(); - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); return MemoryUsage(initial_size(), used, committed, maxSize); } diff --git a/src/hotspot/share/gc/shared/barrierSet.cpp b/src/hotspot/share/gc/shared/barrierSet.cpp index a30b23ce2d9..1fd0317e8f1 100644 --- a/src/hotspot/share/gc/shared/barrierSet.cpp +++ b/src/hotspot/share/gc/shared/barrierSet.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 @@ -86,7 +86,7 @@ BarrierSet::BarrierSet(BarrierSetAssembler* barrier_set_assembler, void BarrierSet::on_thread_attach(Thread* thread) { BarrierSetNMethod* bs_nm = barrier_set_nmethod(); - thread->set_nmethod_disarmed_guard_value(bs_nm->disarmed_guard_value()); + bs_nm->set_thread_disarmed_guard_value(thread); } // Called from init.cpp diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index 2f7b79beab0..c36deb3446a 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp @@ -135,6 +135,10 @@ ByteSize BarrierSetNMethod::thread_disarmed_guard_value_offset() const { return Thread::nmethod_disarmed_guard_value_offset(); } +void BarrierSetNMethod::set_thread_disarmed_guard_value(Thread* thread) { + thread->set_nmethod_disarmed_guard_value(disarmed_guard_value()); +} + class BarrierSetNMethodArmClosure : public ThreadClosure { private: int _disarmed_guard_value; diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp index 812763e429d..cd01ddda09c 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp @@ -54,9 +54,11 @@ public: bool supports_entry_barrier(nmethod* nm); virtual bool nmethod_entry_barrier(nmethod* nm); - virtual ByteSize thread_disarmed_guard_value_offset() const; virtual int* disarmed_guard_value_address() const; + ByteSize thread_disarmed_guard_value_offset() const; + void set_thread_disarmed_guard_value(Thread* thread); + int disarmed_guard_value() const; static int nmethod_stub_entry_barrier(address* return_address_ptr); diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp index 97c24611de4..f90fbc517dc 100644 --- a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp @@ -193,13 +193,9 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { __ membar_acquire(); } - /* Normalize boolean value returned by unsafe operation, i.e., value != 0 ? value = true : value false. */ + // Truncate boolean values returned by unsafe operations. if (mask_boolean) { - LabelObj* equalZeroLabel = new LabelObj(); - __ cmp(lir_cond_equal, result, 0); - __ branch(lir_cond_equal, equalZeroLabel->label()); - __ move(LIR_OprFact::intConst(1), result); - __ branch_destination(equalZeroLabel->label()); + __ logical_and(result, LIR_OprFact::intConst(1), result); } } diff --git a/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp b/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp index 3a974952fea..b0a784e9282 100644 --- a/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCBreakpoints.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 @@ -22,6 +22,7 @@ * */ +#include "code/codeCache.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/concurrentGCBreakpoints.hpp" #include "logging/log.hpp" @@ -89,6 +90,7 @@ void ConcurrentGCBreakpoints::release_control() { MonitorLocker ml(monitor()); log_trace(gc, breakpoint)("release_control"); reset_request_state(); + CodeCache::clear_deferred_unloading_gc_request(); ml.notify_all(); } diff --git a/src/hotspot/share/gc/shared/gcThreadLocalData.hpp b/src/hotspot/share/gc/shared/gcThreadLocalData.hpp index 2847cd8bf33..b0659c58390 100644 --- a/src/hotspot/share/gc/shared/gcThreadLocalData.hpp +++ b/src/hotspot/share/gc/shared/gcThreadLocalData.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 @@ -40,6 +40,6 @@ // should consider placing frequently accessed fields first in // T, so that field offsets relative to Thread are small, which // often allows for a more compact instruction encoding. -typedef uint64_t GCThreadLocalData[40]; // 320 bytes +typedef uint64_t GCThreadLocalData[39]; // 312 bytes #endif // SHARE_GC_SHARED_GCTHREADLOCALDATA_HPP diff --git a/src/hotspot/share/gc/shared/satbMarkQueue.cpp b/src/hotspot/share/gc/shared/satbMarkQueue.cpp index 63496f2eb25..a1bd4e5a9c8 100644 --- a/src/hotspot/share/gc/shared/satbMarkQueue.cpp +++ b/src/hotspot/share/gc/shared/satbMarkQueue.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 @@ -199,7 +199,7 @@ void SATBMarkQueueSet::set_active_all_threads(bool active, bool expected_active) if (_active) { assert(queue.is_empty(), "queues should be empty when activated"); } else { - queue.set_index(queue.current_capacity()); + queue.set_empty(); } queue.set_active(_active); } @@ -363,7 +363,7 @@ size_t SATBMarkQueue::current_capacity() const { } void SATBMarkQueueSet::reset_queue(SATBMarkQueue& queue) { - queue.set_index(queue.current_capacity()); + queue.set_empty(); } void SATBMarkQueueSet::flush_queue(SATBMarkQueue& queue) { diff --git a/src/hotspot/share/gc/shared/satbMarkQueue.hpp b/src/hotspot/share/gc/shared/satbMarkQueue.hpp index f1577c004de..36c287cd64d 100644 --- a/src/hotspot/share/gc/shared/satbMarkQueue.hpp +++ b/src/hotspot/share/gc/shared/satbMarkQueue.hpp @@ -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 @@ -99,6 +99,10 @@ public: _index = index_to_byte_index(new_index); } + void set_empty() { + set_index(current_capacity()); + } + // Returns the capacity of the buffer, or 0 if the queue doesn't currently // have a buffer. size_t current_capacity() const; diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 35749452c85..94d0d904340 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -103,7 +103,7 @@ bool WorkerThreads::allow_inject_creation_failure() const { return false; } - if (_created_workers == 0) { + if (_created_workers.load_relaxed() == 0) { // Never allow creation failures of the first worker, it will cause the VM to exit return false; } @@ -135,18 +135,20 @@ uint WorkerThreads::set_active_workers(uint num_workers) { "Invalid number of active workers %u (should be 1-%u)", num_workers, _max_workers); - while (_created_workers < num_workers) { - WorkerThread* const worker = create_worker(_created_workers); + uint local_created_workers = created_workers(); + while (local_created_workers < num_workers) { + WorkerThread* const worker = create_worker(local_created_workers); if (worker == nullptr) { log_error(gc, task)("Failed to create worker thread"); break; } - _workers[_created_workers] = worker; - _created_workers++; + _workers[local_created_workers] = worker; + local_created_workers++; + _created_workers.release_store(local_created_workers); } - _active_workers = MIN2(_created_workers, num_workers); + _active_workers = MIN2(local_created_workers, num_workers); log_trace(gc, task)("%s: using %d out of %d workers", _name, _active_workers, _max_workers); @@ -154,14 +156,16 @@ uint WorkerThreads::set_active_workers(uint num_workers) { } void WorkerThreads::threads_do(ThreadClosure* tc) const { - for (uint i = 0; i < _created_workers; i++) { + uint local_created_workers = created_workers(); + for (uint i = 0; i < local_created_workers; i++) { tc->do_thread(_workers[i]); } } template void WorkerThreads::threads_do_f(Function function) const { - for (uint i = 0; i < _created_workers; i++) { + uint local_created_workers = created_workers(); + for (uint i = 0; i < local_created_workers; i++) { function(_workers[i]); } } diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index 003ce8a2959..6ed28e5b9b7 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -88,7 +88,11 @@ private: const char* const _name; WorkerThread** _workers; const uint _max_workers; - uint _created_workers; + // _created_workers publishes the initialized prefix of _workers. + // Writers release-store to it after initializing an entry. Readers + // load-acquire before accessing _workers to not access uninitalized + // data. + Atomic _created_workers; uint _active_workers; WorkerTaskDispatcher _dispatcher; @@ -107,7 +111,7 @@ public: bool allow_inject_creation_failure() const; uint max_workers() const { return _max_workers; } - uint created_workers() const { return _created_workers; } + uint created_workers() const { return _created_workers.load_acquire(); } uint active_workers() const { return _active_workers; } uint set_active_workers(uint num_workers); diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 637ed6e6407..de0b838fe45 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -31,8 +31,6 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #ifdef ASSERT @@ -41,43 +39,61 @@ #define __ gen->lir()-> #endif -void ShenandoahPreBarrierStub::emit_code(LIR_Assembler* ce) { +void ShenandoahKeepaliveBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->gen_pre_barrier_stub(ce, this); + bs->keepalive_barrier_c1_stub(ce, this); } void ShenandoahLoadReferenceBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->gen_load_reference_barrier_stub(ce, this); + bs->load_reference_barrier_c1_stub(ce, this); } ShenandoahBarrierSetC1::ShenandoahBarrierSetC1() : - _pre_barrier_c1_runtime_code_blob(nullptr), + _keepalive_barrier_c1_runtime_code_blob(nullptr), _load_reference_barrier_strong_rt_code_blob(nullptr), _load_reference_barrier_strong_native_rt_code_blob(nullptr), _load_reference_barrier_weak_rt_code_blob(nullptr), _load_reference_barrier_phantom_rt_code_blob(nullptr) {} -void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val) { - // First we test whether marking is in progress. +address ShenandoahBarrierSetC1::keepalive_barrier_stub() { + assert(_keepalive_barrier_c1_runtime_code_blob != nullptr, "Must be available"); + return _keepalive_barrier_c1_runtime_code_blob->code_begin(); +} - bool patch = (decorators & C1_NEEDS_PATCHING) != 0; - bool do_load = pre_val == LIR_OprFact::illegalOpr; +address ShenandoahBarrierSetC1::load_reference_barrier_stub(DecoratorSet decorators) { + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + if (is_strong) { + if (is_native) { + assert(_load_reference_barrier_strong_native_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_strong_native_rt_code_blob->code_begin(); + } else { + assert(_load_reference_barrier_strong_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_strong_rt_code_blob->code_begin(); + } + } else if (is_weak) { + assert(_load_reference_barrier_weak_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_weak_rt_code_blob->code_begin(); + } else if (is_phantom) { + assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_phantom_rt_code_blob->code_begin(); + } + ShouldNotReachHere(); + return nullptr; +} + +void ShenandoahBarrierSetC1::enter_if_gc_state(LIRGenerator* gen, int flags, CodeStub* slow_stub) { LIR_Opr thrd = gen->getThreadPointer(); - LIR_Address* gc_state_addr = - new LIR_Address(thrd, - in_bytes(ShenandoahThreadLocalData::gc_state_offset()), - T_BYTE); - // Read the gc_state flag. LIR_Opr flag_val = gen->new_register(T_INT); - __ load(gc_state_addr, flag_val); - - // Create a mask to test if the marking bit is set. - LIR_Opr mask = LIR_OprFact::intConst(ShenandoahHeap::MARKING); LIR_Opr mask_reg = gen->new_register(T_INT); - __ move(mask, mask_reg); + LIR_Address* gc_state_addr = new LIR_Address(thrd, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), T_BYTE); + __ load(gc_state_addr, flag_val); + __ move(LIR_OprFact::intConst(flags), mask_reg); if (two_operand_lir_form) { __ logical_and(flag_val, mask_reg, flag_val); } else { @@ -86,91 +102,54 @@ void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, flag_val = masked_flag; } __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); + __ branch(lir_cond_notEqual, slow_stub); + __ branch_destination(slow_stub->continuation()); +} - LIR_PatchCode pre_val_patch_code = lir_patch_none; +void ShenandoahBarrierSetC1::keepalive_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { + CodeStub* slow_stub; + if (obj == LIR_OprFact::illegalOpr) { + // Caller wants us to do the load. + obj = gen->new_register(T_OBJECT); - CodeStub* slow; - - if (do_load) { - assert(pre_val == LIR_OprFact::illegalOpr, "sanity"); - assert(addr_opr != LIR_OprFact::illegalOpr, "sanity"); - - if (patch) - pre_val_patch_code = lir_patch_normal; - - pre_val = gen->new_register(T_OBJECT); - - if (!addr_opr->is_address()) { - assert(addr_opr->is_register(), "must be"); - addr_opr = LIR_OprFact::address(new LIR_Address(addr_opr, T_OBJECT)); + assert(addr != LIR_OprFact::illegalOpr, "sanity"); + if (!addr->is_address()) { + assert(addr->is_register(), "must be"); + addr = LIR_OprFact::address(new LIR_Address(addr, T_OBJECT)); } - slow = new ShenandoahPreBarrierStub(addr_opr, pre_val, pre_val_patch_code, info ? new CodeEmitInfo(info) : nullptr); - } else { - assert(addr_opr == LIR_OprFact::illegalOpr, "sanity"); - assert(pre_val->is_register(), "must be"); - assert(pre_val->type() == T_OBJECT, "must be an object"); - slow = new ShenandoahPreBarrierStub(pre_val); + slow_stub = new ShenandoahKeepaliveBarrierStub(obj, addr); + } else { + // Caller gave us the obj to work with. + assert(addr == LIR_OprFact::illegalOpr, "sanity"); + assert(obj->is_register(), "must be"); + assert(obj->type() == T_OBJECT, "must be an object"); + + slow_stub = new ShenandoahKeepaliveBarrierStub(obj); } - __ branch(lir_cond_notEqual, slow); - __ branch_destination(slow->continuation()); + enter_if_gc_state(gen, ShenandoahHeap::MARKING, slow_stub); } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { - if (ShenandoahLoadRefBarrier) { - return load_reference_barrier_impl(gen, obj, addr, decorators); - } else { - return obj; - } -} - -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { +void ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); obj = ensure_in_register(gen, obj, T_OBJECT); - assert(obj->is_register(), "must be a register at this point"); addr = ensure_in_register(gen, addr, T_ADDRESS); + assert(obj->is_register(), "must be a register at this point"); assert(addr->is_register(), "must be a register at this point"); - LIR_Opr result = gen->result_register_for(obj->value_type()); - LIR_Opr tmp1 = gen->new_register(T_ADDRESS); - LIR_Opr tmp2 = gen->new_register(T_ADDRESS); - LIR_Opr thrd = gen->getThreadPointer(); - LIR_Address* active_flag_addr = - new LIR_Address(thrd, - in_bytes(ShenandoahThreadLocalData::gc_state_offset()), - T_BYTE); - // Read and check the gc-state-flag. - LIR_Opr flag_val = gen->new_register(T_INT); - __ load(active_flag_addr, flag_val); + // Barrier slowpaths return value in this register. Declare it in the stub + // as clobbered. The obj would remain as result for both fast- and slow-paths. + LIR_Opr slow_result = gen->result_register_for(obj->value_type()); + + CodeStub* slow_stub = new ShenandoahLoadReferenceBarrierStub(obj, addr, slow_result, decorators); + int flags = ShenandoahHeap::HAS_FORWARDED; if (!ShenandoahBarrierSet::is_strong_access(decorators)) { flags |= ShenandoahHeap::WEAK_ROOTS; } - LIR_Opr mask = LIR_OprFact::intConst(flags); - LIR_Opr mask_reg = gen->new_register(T_INT); - __ move(mask, mask_reg); - - if (two_operand_lir_form) { - __ logical_and(flag_val, mask_reg, flag_val); - } else { - LIR_Opr masked_flag = gen->new_register(T_INT); - __ logical_and(flag_val, mask_reg, masked_flag); - flag_val = masked_flag; - } - __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); - - CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, decorators); - __ branch(lir_cond_notEqual, slow); - - // No barrier is needed, move obj to result now. - __ move(obj, result); - - // Slow-path re-enters here with result set. - __ branch_destination(slow->continuation()); - - return result; + enter_if_gc_state(gen, flags, slow_stub); } LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type) { @@ -189,21 +168,21 @@ LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr ob } void ShenandoahBarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { - if (access.is_oop()) { - if (ShenandoahSATBBarrier) { - pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), access.resolved_addr(), LIR_OprFact::illegalOpr /* pre_val */); - } + DecoratorSet decorators = access.decorators(); + LIRGenerator* gen = access.gen(); + + if (ShenandoahSATBBarrier && access.is_oop()) { + keepalive_barrier(gen, /* obj = */ LIR_OprFact::illegalOpr, /* addr = */ access.resolved_addr(), decorators); } BarrierSetC1::store_at_resolved(access, value); if (ShenandoahCardBarrier && access.is_oop()) { - DecoratorSet decorators = access.decorators(); bool is_array = (decorators & IS_ARRAY) != 0; bool on_anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool precise = is_array || on_anonymous; LIR_Opr post_addr = precise ? access.resolved_addr() : access.base().opr(); - post_barrier(access, post_addr); + card_barrier(gen, post_addr, decorators); } } @@ -230,7 +209,7 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { LIR_Opr tmp = gen->new_register(T_OBJECT); BarrierSetC1::load_at_resolved(access, tmp); - tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); + load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); __ move(tmp, result); } else { BarrierSetC1::load_at_resolved(access, result); @@ -246,18 +225,17 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) Lcont_anonymous = new LabelObj(); generate_referent_check(access, Lcont_anonymous); } - pre_barrier(gen, access.access_emit_info(), decorators, LIR_OprFact::illegalOpr /* addr_opr */, - result /* pre_val */); + keepalive_barrier(gen, /* obj = */ result, /* addr = */ LIR_OprFact::illegalOpr, decorators); if (is_anonymous) { __ branch_destination(Lcont_anonymous->label()); } } } -class C1ShenandoahPreBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { +class C1ShenandoahKeepaliveBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_pre_barrier_runtime_stub(sasm); + bs->keepalive_barrier_c1_runtime_stub(sasm); return nullptr; } }; @@ -271,18 +249,20 @@ public: virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _decorators); + bs->load_reference_barrier_c1_runtime_stub(sasm, _decorators); return nullptr; } }; bool ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) { - C1ShenandoahPreBarrierCodeGenClosure pre_code_gen_cl; - _pre_barrier_c1_runtime_code_blob = Runtime1::generate_blob(buffer_blob, StubId::NO_STUBID, - "shenandoah_pre_barrier_slow", - false, &pre_code_gen_cl); - if (_pre_barrier_c1_runtime_code_blob == nullptr) { - return false; + if (ShenandoahSATBBarrier) { + C1ShenandoahKeepaliveBarrierCodeGenClosure keepalive_code_gen_cl; + _keepalive_barrier_c1_runtime_code_blob = Runtime1::generate_blob(buffer_blob, StubId::NO_STUBID, + "shenandoah_keepalive_barrier_slow", + false, &keepalive_code_gen_cl); + if (_keepalive_barrier_c1_runtime_code_blob == nullptr) { + return false; + } } if (ShenandoahLoadRefBarrier) { C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_strong_code_gen_cl(ON_STRONG_OOP_REF); @@ -318,11 +298,9 @@ bool ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) return true; } -void ShenandoahBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr) { +void ShenandoahBarrierSetC1::card_barrier(LIRGenerator* gen, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); - DecoratorSet decorators = access.decorators(); - LIRGenerator* gen = access.gen(); bool in_heap = (decorators & IN_HEAP) != 0; if (!in_heap) { return; @@ -378,6 +356,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI return BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); } + DecoratorSet decorators = access.decorators(); LIRGenerator* gen = access.gen(); LIR_Opr tmp = gen->new_register(T_OBJECT); @@ -386,22 +365,20 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI // Handle the previous value through SATB, as we are about to perform the store. __ load(addr->as_address_ptr(), tmp); if (ShenandoahSATBBarrier) { - pre_barrier(gen, access.access_emit_info(), access.decorators(), - /* addr_opr (unused) = */ LIR_OprFact::illegalOpr, - /* pre_val = */ tmp); + keepalive_barrier(gen, /* obj = */ tmp, /* addr = */ LIR_OprFact::illegalOpr, decorators); } // Perform LRB on location to fix it up for this and all following accesses. // This guarantees there are no false negatives due to concurrent evacuation, // and the value loaded later by CAS is sanitized by some LRB, or is null. if (ShenandoahLoadRefBarrier) { - load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, access.decorators()); + load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, decorators); } LIR_Opr result = BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); if (ShenandoahCardBarrier) { - post_barrier(access, /* addr = */ addr); + card_barrier(gen, /* addr = */ addr, decorators); } return result; @@ -412,6 +389,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt return BarrierSetC1::atomic_xchg_at_resolved(access, value); } + DecoratorSet decorators = access.decorators(); LIRGenerator* gen = access.gen(); LIR_Opr tmp = gen->new_register(T_OBJECT); @@ -420,22 +398,20 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt // Handle the previous value through SATB, as we are about to perform the store. __ load(addr->as_address_ptr(), tmp); if (ShenandoahSATBBarrier) { - pre_barrier(gen, access.access_emit_info(), access.decorators(), - /* addr_opr (unused) = */ LIR_OprFact::illegalOpr, - /* pre_val = */ tmp); + keepalive_barrier(gen, /* obj = */ tmp, /* addr = */ LIR_OprFact::illegalOpr, decorators); } // Perform LRB on location to fix it up for this and all following accesses. // This is purely opportunistic: we would not have any false negatives here. // This guarantees the value loaded later by XCHG is sanitized by some LRB, or is null. if (ShenandoahLoadRefBarrier) { - load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, access.decorators()); + load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, decorators); } LIR_Opr result = BarrierSetC1::atomic_xchg_at_resolved(access, value); if (ShenandoahCardBarrier) { - post_barrier(access, /* addr = */ addr); + card_barrier(gen, /* addr = */ addr, decorators); } return result; diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 413777a61ee..3f064c3569b 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -29,63 +29,48 @@ #include "c1/c1_CodeStubs.hpp" #include "gc/shared/c1/barrierSetC1.hpp" -class ShenandoahPreBarrierStub: public CodeStub { +class ShenandoahKeepaliveBarrierStub: public CodeStub { friend class ShenandoahBarrierSetC1; private: - bool _do_load; + LIR_Opr _obj; LIR_Opr _addr; - LIR_Opr _pre_val; - LIR_PatchCode _patch_code; - CodeEmitInfo* _info; + bool _do_load; public: - // Version that _does_ generate a load of the previous value from addr. - // addr (the address of the field to be read) must be a LIR_Address - // pre_val (a temporary register) must be a register; - ShenandoahPreBarrierStub(LIR_Opr addr, LIR_Opr pre_val, LIR_PatchCode patch_code, CodeEmitInfo* info) : - _do_load(true), _addr(addr), _pre_val(pre_val), - _patch_code(patch_code), _info(info) + ShenandoahKeepaliveBarrierStub(LIR_Opr obj, LIR_Opr addr) : + _obj(obj), _addr(addr), _do_load(true) { - assert(_pre_val->is_register(), "should be temporary register"); + assert(_obj->is_register(), "should be temporary register"); assert(_addr->is_address(), "should be the address of the field"); FrameMap* f = Compilation::current()->frame_map(); - f->update_reserved_argument_area_size(2 * BytesPerWord); + f->update_reserved_argument_area_size(1 * BytesPerWord); } - // Version that _does not_ generate load of the previous value; the - // previous value is assumed to have already been loaded into pre_val. - ShenandoahPreBarrierStub(LIR_Opr pre_val) : - _do_load(false), _addr(LIR_OprFact::illegalOpr), _pre_val(pre_val), - _patch_code(lir_patch_none), _info(nullptr) + ShenandoahKeepaliveBarrierStub(LIR_Opr obj) : + _obj(obj), _addr(LIR_OprFact::illegalOpr), _do_load(false) { - assert(_pre_val->is_register(), "should be a register"); + assert(_obj->is_register(), "should be a register"); + FrameMap* f = Compilation::current()->frame_map(); + f->update_reserved_argument_area_size(1 * BytesPerWord); } LIR_Opr addr() const { return _addr; } - LIR_Opr pre_val() const { return _pre_val; } - LIR_PatchCode patch_code() const { return _patch_code; } - CodeEmitInfo* info() const { return _info; } + LIR_Opr obj() const { return _obj; } bool do_load() const { return _do_load; } virtual void emit_code(LIR_Assembler* e); virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(); if (_do_load) { - // don't pass in the code emit info since it's processed in the fast - // path - if (_info != nullptr) - visitor->do_slow_case(_info); - else - visitor->do_slow_case(); - visitor->do_input(_addr); - visitor->do_temp(_pre_val); + visitor->do_temp(_addr); + visitor->do_temp(_obj); } else { - visitor->do_slow_case(); - visitor->do_input(_pre_val); + visitor->do_input(_obj); } } #ifndef PRODUCT - virtual void print_name(outputStream* out) const { out->print("ShenandoahPreBarrierStub"); } + virtual void print_name(outputStream* out) const { out->print("ShenandoahKeepaliveBarrierStub"); } #endif // PRODUCT }; @@ -94,29 +79,21 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { private: LIR_Opr _obj; LIR_Opr _addr; - LIR_Opr _result; - LIR_Opr _tmp1; - LIR_Opr _tmp2; + LIR_Opr _slow_result; DecoratorSet _decorators; public: - ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, DecoratorSet decorators) : - _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _decorators(decorators) + ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr slow_result, DecoratorSet decorators) : + _obj(obj), _addr(addr), _slow_result(slow_result), _decorators(decorators) { assert(_obj->is_register(), "should be register"); assert(_addr->is_register(), "should be register"); - assert(_result->is_register(), "should be register"); - assert(_tmp1->is_register(), "should be register"); - assert(_tmp2->is_register(), "should be register"); - FrameMap* f = Compilation::current()->frame_map(); f->update_reserved_argument_area_size(2 * BytesPerWord); } LIR_Opr obj() const { return _obj; } LIR_Opr addr() const { return _addr; } - LIR_Opr result() const { return _result; } - LIR_Opr tmp1() const { return _tmp1; } - LIR_Opr tmp2() const { return _tmp2; } + LIR_Opr slow_result() const { return _slow_result; } DecoratorSet decorators() const { return _decorators; } virtual void emit_code(LIR_Assembler* e); @@ -124,12 +101,10 @@ public: visitor->do_slow_case(); visitor->do_input(_obj); visitor->do_temp(_obj); + visitor->do_output(_obj); visitor->do_input(_addr); visitor->do_temp(_addr); - visitor->do_temp(_result); - visitor->do_output(_result); - visitor->do_temp(_tmp1); - visitor->do_temp(_tmp2); + visitor->do_temp(_slow_result); } #ifndef PRODUCT virtual void print_name(outputStream* out) const { out->print("ShenandoahLoadReferenceBarrierStub"); } @@ -138,63 +113,35 @@ public: class ShenandoahBarrierSetC1 : public BarrierSetC1 { private: - CodeBlob* _pre_barrier_c1_runtime_code_blob; + CodeBlob* _keepalive_barrier_c1_runtime_code_blob; CodeBlob* _load_reference_barrier_strong_rt_code_blob; CodeBlob* _load_reference_barrier_strong_native_rt_code_blob; CodeBlob* _load_reference_barrier_weak_rt_code_blob; CodeBlob* _load_reference_barrier_phantom_rt_code_blob; - void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); + void enter_if_gc_state(LIRGenerator* gen, int flags, CodeStub* slow_stub); - LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); - - LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void keepalive_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void card_barrier(LIRGenerator* gen, LIR_Opr addr, DecoratorSet decorators); LIR_Opr ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type); public: ShenandoahBarrierSetC1(); - CodeBlob* pre_barrier_c1_runtime_code_blob() { - assert(_pre_barrier_c1_runtime_code_blob != nullptr, ""); - return _pre_barrier_c1_runtime_code_blob; - } + address keepalive_barrier_stub(); + address load_reference_barrier_stub(DecoratorSet decorators); - CodeBlob* load_reference_barrier_strong_rt_code_blob() { - assert(_load_reference_barrier_strong_rt_code_blob != nullptr, ""); - return _load_reference_barrier_strong_rt_code_blob; - } - - CodeBlob* load_reference_barrier_strong_native_rt_code_blob() { - assert(_load_reference_barrier_strong_native_rt_code_blob != nullptr, ""); - return _load_reference_barrier_strong_native_rt_code_blob; - } - - CodeBlob* load_reference_barrier_weak_rt_code_blob() { - assert(_load_reference_barrier_weak_rt_code_blob != nullptr, ""); - return _load_reference_barrier_weak_rt_code_blob; - } - - CodeBlob* load_reference_barrier_phantom_rt_code_blob() { - assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, ""); - return _load_reference_barrier_phantom_rt_code_blob; - } + virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob); protected: - virtual void store_at_resolved(LIRAccess& access, LIR_Opr value); virtual LIR_Opr resolve_address(LIRAccess& access, bool resolve_in_register); virtual void load_at_resolved(LIRAccess& access, LIR_Opr result); virtual LIR_Opr atomic_cmpxchg_at_resolved(LIRAccess& access, LIRItem& cmp_value, LIRItem& new_value); - virtual LIR_Opr atomic_xchg_at_resolved(LIRAccess& access, LIRItem& value); - - void post_barrier(LIRAccess& access, LIR_Opr addr); - -public: - - virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob); }; #endif // SHARE_GC_SHENANDOAH_C1_SHENANDOAHBARRIERSETC1_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 11ca6ff3e90..4cd8af1370d 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -211,9 +211,11 @@ void ShenandoahAdaptiveHeuristics::record_success_concurrent() { } } -void ShenandoahAdaptiveHeuristics::record_degenerated() { - ShenandoahHeuristics::record_degenerated(); - add_degenerated_gc_time(_precursor_cycle_start, elapsed_degenerated_cycle_time()); +void ShenandoahAdaptiveHeuristics::record_degenerated(bool is_generational_global) { + ShenandoahHeuristics::record_degenerated(is_generational_global); + if (!is_generational_global) { + add_degenerated_gc_time(_precursor_cycle_start, elapsed_degenerated_cycle_time()); + } } bool ShenandoahAdaptiveHeuristics::should_start_gc() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 75eb3a7facb..b571f81140c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -57,7 +57,7 @@ public: void start_idle_span() override; void record_success_concurrent() override; - void record_degenerated() override; + void record_degenerated(bool is_generational_global) override; bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 2f247db2951..4dacd20d0c7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -30,6 +30,7 @@ #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahTrace.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" @@ -151,6 +152,8 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec if (immediate_percent <= ShenandoahImmediateThreshold) { choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); + } else if (heap->mode()->is_generational()) { + adjust_reserves_for_abbreviated(heap); } collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); ShenandoahTracer::report_evacuation_info(collection_set, free_regions, immediate_regions, immediate_garbage); @@ -160,6 +163,13 @@ void ShenandoahHeuristics::start_idle_span() { // do nothing } +void ShenandoahHeuristics::adjust_reserves_for_abbreviated(ShenandoahHeap* heap) { + // We are not going to evacuate because this is an abbreviated cycle. Reset the reserves. + heap->young_generation()->set_evacuation_reserve(0UL); + heap->old_generation()->set_evacuation_reserve(0UL); + heap->old_generation()->set_promoted_reserve(0UL); +} + void ShenandoahHeuristics::record_degenerated_cycle_start(bool out_of_cycle) { if (out_of_cycle) { _precursor_cycle_start = _cycle_start = os::elapsedTime(); @@ -259,7 +269,15 @@ void ShenandoahHeuristics::record_success_concurrent() { adjust_penalty(Concurrent_Adjust); } -void ShenandoahHeuristics::record_degenerated() { +void ShenandoahHeuristics::record_degenerated(bool is_generational_global) { + + if (!is_generational_global) { + // We don't penalize generational GC heuristics for global GC because heuristics predict based on assumption of young GC. + _most_recent_declined_trigger_count = _declined_trigger_count; + _declined_trigger_count = 0; + } else { + _most_recent_declined_trigger_count = _declined_trigger_count = 0; + } adjust_penalty(Degenerated_Penalty); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 3f3b3898f54..b50c2004262 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -57,6 +57,7 @@ } while (0) class ShenandoahCollectionSet; +class ShenandoahHeap; class ShenandoahHeapRegion; /* @@ -82,6 +83,9 @@ private: double _most_recent_trigger_evaluation_time; double _most_recent_planned_sleep_interval; + // When we decide to do an abbreviated cycle, withdraw reserves so memory can be made available to mutators. + void adjust_reserves_for_abbreviated(ShenandoahHeap* heap); + protected: static constexpr uint Moving_Average_Samples = 10; // Number of samples to store in moving averages @@ -195,8 +199,6 @@ protected: virtual void adjust_penalty(intx step); inline void accept_trigger() { - _most_recent_declined_trigger_count = _declined_trigger_count; - _declined_trigger_count = 0; _start_gc_is_pending = true; } @@ -254,7 +256,7 @@ public: virtual void record_success_concurrent(); - virtual void record_degenerated(); + virtual void record_degenerated(bool is_generational_global); virtual void record_success_full(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 4f820f26075..333451a8125 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -856,10 +856,10 @@ void ShenandoahOldHeuristics::record_success_concurrent() { this->ShenandoahHeuristics::record_success_concurrent(); } -void ShenandoahOldHeuristics::record_degenerated() { +void ShenandoahOldHeuristics::record_degenerated(bool is_generational_global) { // Forget any triggers that occurred while OLD GC was ongoing. If we really need to start another, it will retrigger. clear_triggers(); - this->ShenandoahHeuristics::record_degenerated(); + this->ShenandoahHeuristics::record_degenerated(is_generational_global); } void ShenandoahOldHeuristics::record_success_full() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index 04a92d28248..37d186a991f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -246,7 +246,7 @@ public: void record_success_concurrent() override; - void record_degenerated() override; + void record_degenerated(bool is_generational_global) override; void record_success_full() override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp index ff0a425e260..ca94b91200a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHALLOCRATE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHALLOCRATE_HPP +#include "gc/shenandoah/shenandoahStripedCounter.hpp" #include "gc/shenandoah/shenandoahWeightedSeq.hpp" #include "runtime/atomic.hpp" #include "runtime/mutex.hpp" @@ -110,10 +111,26 @@ class ShenandoahAllocRate { static constexpr size_t ALLOC_SAMPLE_MAX = G; PaddedMonitor _sample_lock; - Atomic _allocated_bytes_since_last_sample; - Atomic _minimum_sample_size; // bytes, read by mutator, updated by gc + ShenandoahStripedCounter _unsampled; + // Packed minimum_sample_size and log_per_stripe_threshold for one alloc-path load. + Atomic _sample_params; jlong _last_sample_time; + static uint64_t encode_sample_params(const uint32_t minimum_sample_size, const uint32_t log_per_stripe_threshold) { + return (static_cast(log_per_stripe_threshold) << 32) | + minimum_sample_size; + } + + static size_t decode_min_sample_size(const uint64_t params) { + return static_cast(params); + } + + static uint32_t decode_log_per_stripe_threshold(const uint64_t params) { + return static_cast(params >> 32); + } + + void maybe_take_sample(size_t minimum_sample_size, size_t striped_unsampled); + ShenandoahWeightedSeq _baseline; ShenandoahWeightedSeq _recent; ShenandoahWeightedSeq _momentary; @@ -124,22 +141,19 @@ public: const uint recent_window_size = ShenandoahRecentAllocRateSampleWindow, const uint momentary_window_size = ShenandoahMomentaryAllocRateSampleWindow) : _sample_lock(Mutex::nosafepoint - 2, "ShenandoahAllocSample_lock", true) - , _allocated_bytes_since_last_sample(0) - , _minimum_sample_size(minimum_sample_size) , _last_sample_time(Clock::elapsed_counter()) , _baseline(baseline_window_size) , _recent(recent_window_size) , _momentary(momentary_window_size) { + set_minimum_sample_size(minimum_sample_size); } // Update minimum sample size based on the given available bytes void update_minimum_sample_size(size_t available); - // Set minimum sample size in bytes - void set_minimum_sample_size(const size_t minimum_sample_size) { - _minimum_sample_size.store_relaxed(minimum_sample_size); - } + // Set minimum sample size and its per-stripe trigger shift. + void set_minimum_sample_size(size_t minimum_sample_size); // Indicate that this many bytes have been allocated (by the mutator). void allocated(size_t allocated_bytes); @@ -170,11 +184,29 @@ public: } private: + // Log2 of the per-stripe trigger threshold. + uint32_t log_per_stripe_threshold_for(size_t minimum_sample_size) const; + + // Fast, lock-free: did this add carry the calling thread's stripe across a per-stripe threshold + // multiple? The threshold is a power of two, so a crossing is a change in the bits above it. + static bool striped_threshold_exceeded(size_t striped_unsampled, size_t previous_striped_unsampled, uint32_t log_per_stripe_threshold) { + return (striped_unsampled >> log_per_stripe_threshold) > (previous_striped_unsampled >> log_per_stripe_threshold); + } + + // Whether the unsampled bytes are still below the sampling floor. Must be called under the sample + // lock: drains only happen under the lock, so reading the live stripe value and sum() here filters + // out false positives from a concurrent drain that already reset the counter. + bool unsampled_below_floor(size_t minimum_sample_size, size_t striped_unsampled) const { + assert(_sample_lock.owned_by_self(), "Caller must hold lock"); + return (_unsampled.num_stripes() > 1 && _unsampled.current_stripe_value() < striped_unsampled) || + _unsampled.sum() < minimum_sample_size; + } + // Record the sample under the sample lock void take_sample(jlong now, jlong elapsed, size_t unsampled); double upper_bound_no_lock(const double standard_deviations) const { - assert(_sample_lock.is_locked(), "Caller must hold lock"); + assert(_sample_lock.owned_by_self(), "Caller must hold lock"); return _baseline.weighted_average() + standard_deviations * _baseline.weighted_sd(); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp index eedda19b86e..e317721cd7b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp @@ -27,8 +27,10 @@ #include "gc/shenandoah/shenandoahAllocRate.hpp" +#include "gc/shenandoah/shenandoahStripedCounter.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "logging/log.hpp" +#include "utilities/powerOfTwo.hpp" inline size_t ShenandoahAnticipatedConsumption::baseline_consumption() const { @@ -56,40 +58,56 @@ void ShenandoahAllocRate::update_minimum_sample_size(const size_t availab } template -void ShenandoahAllocRate::allocated(const size_t allocated_bytes) { - size_t unsampled = _allocated_bytes_since_last_sample.add_then_fetch(allocated_bytes); - const size_t minimum_sample_size = _minimum_sample_size.load_relaxed(); - if (unsampled < minimum_sample_size) { - // Not enough to sample yet - return; - } +uint32_t ShenandoahAllocRate::log_per_stripe_threshold_for(const size_t minimum_sample_size) const { + // Floor-log2 of the per-stripe share. Clamps to 0 for a 1-byte trigger. + const int log_threshold = log2i(minimum_sample_size) - (int) _unsampled.log_num_stripes(); + return log_threshold > 0 ? (uint32_t) log_threshold : 0u; +} +template +void ShenandoahAllocRate::set_minimum_sample_size(const size_t minimum_sample_size) { + assert(minimum_sample_size > 0, "minimum sample size must be non-zero"); + _sample_params.store_relaxed(encode_sample_params(checked_cast(minimum_sample_size), log_per_stripe_threshold_for(minimum_sample_size))); +} + +template +void ShenandoahAllocRate::maybe_take_sample(const size_t minimum_sample_size, const size_t striped_unsampled) { if (!_sample_lock.try_lock()) { - // Another thread has the lock and will take the sample + // Another thread has the lock and will take the sample. return; } - unsampled = _allocated_bytes_since_last_sample.load_relaxed(); - if (unsampled < minimum_sample_size) { - // Another thread has sampled and reset the allocated bytes under the lock + if (unsampled_below_floor(minimum_sample_size, striped_unsampled)) { + // Either another thread already sampled and drained, or this thread's stripe crossed its share + // while the aggregate is still short (skewed distribution). Wait for more. _sample_lock.unlock(); return; } - const jlong now = Clock::elapsed_counter(); const jlong elapsed = now - _last_sample_time; - if (elapsed <= 0) { - // Avoid sampling nonsense allocation rates + // Avoid sampling nonsense allocation rates. _sample_lock.unlock(); return; } - - take_sample(now, elapsed, unsampled); - + take_sample(now, elapsed, _unsampled.drain()); _sample_lock.unlock(); } +template +void ShenandoahAllocRate::allocated(const size_t allocated_bytes) { + const size_t striped_unsampled = _unsampled.add(allocated_bytes); + const size_t previous_striped_unsampled = striped_unsampled - allocated_bytes; + + const uint64_t params = _sample_params.load_relaxed(); + const uint32_t log_per_stripe_threshold = decode_log_per_stripe_threshold(params); + + // Re-arm the trigger at every per-stripe threshold crossing. + if (striped_threshold_exceeded(striped_unsampled, previous_striped_unsampled, log_per_stripe_threshold)) { + maybe_take_sample(decode_min_sample_size(params), striped_unsampled); + } +} + template void ShenandoahAllocRate::force_update() { if (!_sample_lock.try_lock()) { @@ -97,7 +115,6 @@ void ShenandoahAllocRate::force_update() { return; } - const size_t unsampled = _allocated_bytes_since_last_sample.load_relaxed(); const jlong now = Clock::elapsed_counter(); const jlong elapsed = now - _last_sample_time; @@ -107,21 +124,17 @@ void ShenandoahAllocRate::force_update() { return; } - take_sample(now, elapsed, unsampled); + take_sample(now, elapsed, _unsampled.drain()); _sample_lock.unlock(); } template void ShenandoahAllocRate::take_sample(jlong now, jlong elapsed, size_t unsampled) { - assert(_sample_lock.is_locked(), "Caller must hold lock"); + assert(_sample_lock.owned_by_self(), "Caller must hold lock"); _last_sample_time = now; - // We are recording this sample, deduct it from the counter. It may be increased - // concurrently by other threads outside the lock, so we still use an atomic access. - _allocated_bytes_since_last_sample.sub_then_fetch(unsampled); - const double timestamp = static_cast(_last_sample_time) / Clock::elapsed_frequency(); const double rate_seconds = static_cast(unsampled) * Clock::elapsed_frequency() / elapsed; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp new file mode 100644 index 00000000000..38f0c7ce045 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp @@ -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 "gc/shenandoah/shenandoahAllocator.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" + +ShenandoahAllocator::ShenandoahAllocator(ShenandoahFreeSet* free_set) + : _free_set(free_set), + _mutator_alloc(free_set), + _collector_alloc(free_set), + _old_collector_alloc(free_set) {} + +HeapWord* ShenandoahAllocator::allocate(ShenandoahAllocRequest& req, bool& in_new_region) { + if (ShenandoahHeapRegion::requires_humongous(req.size())) { + ShenandoahHeapLocker locker(ShenandoahHeap::heap()->lock(), req.is_mutator_alloc()); + switch (req.type()) { + case ShenandoahAllocRequest::_alloc_shared: + case ShenandoahAllocRequest::_alloc_shared_gc: + in_new_region = true; + return _free_set->allocate_contiguous(req, /* is_humongous = */ true); + case ShenandoahAllocRequest::_alloc_cds: + in_new_region = true; + return _free_set->allocate_contiguous(req, /* is_humongous = */ false); + default: + ShouldNotReachHere(); + in_new_region = false; + return nullptr; + } + } + + // Route to the appropriate per-partition allocator. + if (req.is_mutator_alloc()) { + return _mutator_alloc.allocate(req, in_new_region); + } else if (req.is_old()) { + return _old_collector_alloc.allocate(req, in_new_region); + } else { + return _collector_alloc.allocate(req, in_new_region); + } +} + +void ShenandoahAllocator::release_alloc_regions() { + _mutator_alloc.release_alloc_region(); + _collector_alloc.release_alloc_region(); + _old_collector_alloc.release_alloc_region(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp new file mode 100644 index 00000000000..690f97e985e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp @@ -0,0 +1,63 @@ +/* + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHALLOCATOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHALLOCATOR_HPP + +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahPartitionAllocator.hpp" +#include "memory/allocation.hpp" + +typedef ShenandoahPartitionAllocator ShenandoahMutatorAllocator; +typedef ShenandoahPartitionAllocator ShenandoahCollectorAllocator; +typedef ShenandoahPartitionAllocator ShenandoahOldCollectorAllocator; + +// ShenandoahAllocator is the single entry point for memory allocations. Humongous +// requests are served directly via ShenandoahFreeSet; all other requests are routed +// to the appropriate per-partition allocator (mutator, collector, or old-collector). +// Both paths run under the heap lock. +class ShenandoahAllocator : public CHeapObj { +private: + ShenandoahFreeSet* _free_set; + ShenandoahMutatorAllocator _mutator_alloc; + ShenandoahCollectorAllocator _collector_alloc; + ShenandoahOldCollectorAllocator _old_collector_alloc; + +public: + ShenandoahAllocator(ShenandoahFreeSet* free_set); + + // Allocate memory from heap for a request. Humongous requests are served directly via + // ShenandoahFreeSet; all other requests are routed to the mutator, collector, or + // old-collector partition allocator based on request type. The heap lock is taken + // on both paths (here for humongous, inside the partition allocator otherwise). + // Returns nullptr if the request cannot be satisfied. Sets in_new_region to indicate + // whether the returned address is the first allocation in a freshly acquired region. + HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); + + // Release the cached alloc region in every partition allocator. Call before the + // free set is rebuilt, since rebuild may reclassify region affiliation/membership. + void release_alloc_regions(); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHALLOCATOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index 278c04b35d6..2eceeb2eca6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -31,6 +31,7 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" +#include "runtime/orderAccess.hpp" #include "runtime/os.hpp" #include "utilities/vmError.hpp" @@ -425,6 +426,15 @@ void ShenandoahAsserts::assert_marked_strong(void *interior_loc, oop obj, const } } +void ShenandoahAsserts::assert_bitmap_clear_above_top(ShenandoahHeapRegion* region) { + ShenandoahMarkingContext* const ctx = ShenandoahHeap::heap()->marking_context(); + const HeapWord* top_bitmap = ctx->top_bitmap(region); + // Make sure that top is loaded before any of the marks from the bitmap are loaded. If another + // thread has cleared the bitmap we must not allow any stale reads. + OrderAccess::loadload(); + assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, region->end()), "Bitmap above top_bitmap() must be clear"); +} + void ShenandoahAsserts::assert_mark_complete(HeapWord* obj, const char* file, int line) { const ShenandoahHeap* heap = ShenandoahHeap::heap(); const ShenandoahHeapRegion* region = heap->heap_region_containing(obj); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp index 545415a6531..44330d54303 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp @@ -31,6 +31,8 @@ #include "runtime/mutex.hpp" #include "utilities/formatBuffer.hpp" +class ShenandoahHeapRegion; + typedef FormatBuffer<8192> ShenandoahMessageBuffer; class ShenandoahAsserts { @@ -65,6 +67,7 @@ public: static void assert_marked(void* interior_loc, oop obj, const char* file, int line); static void assert_marked_weak(void* interior_loc, oop obj, const char* file, int line); static void assert_marked_strong(void* interior_loc, oop obj, const char* file, int line); + static void assert_bitmap_clear_above_top(ShenandoahHeapRegion* region); // Assert that marking is complete for the generation where this obj resides static void assert_mark_complete(HeapWord* obj, const char* file, int line); @@ -137,6 +140,9 @@ public: #define shenandoah_assert_marked_strong(interior_loc, obj) \ ShenandoahAsserts::assert_marked_strong(interior_loc, obj, __FILE__, __LINE__) +#define shenandoah_assert_clear_above_top(region) \ + ShenandoahAsserts::assert_bitmap_clear_above_top(region) + #define shenandoah_assert_mark_complete(obj) \ ShenandoahAsserts::assert_mark_complete(obj, __FILE__, __LINE__) @@ -227,6 +233,7 @@ public: #define shenandoah_assert_marked_strong_except(interior_loc, obj, exception) #define shenandoah_assert_marked_strong(interior_loc, obj) +#define shenandoah_assert_clear_above_top(region) #define shenandoah_assert_mark_complete(obj) #define shenandoah_assert_in_cset_if(interior_loc, obj, condition) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 8f8dbc9ac83..ac896f24739 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -33,6 +33,7 @@ class ShenandoahHeap; class ShenandoahBarrierSetAssembler; class ShenandoahCardTable; +class ShenandoahMarkingContext; class ShenandoahBarrierSet: public BarrierSet { private: @@ -93,9 +94,6 @@ public: void on_thread_attach(Thread* thread) override; void on_thread_detach(Thread* thread) override; - static inline oop resolve_forwarded_not_null(oop p); - static inline oop resolve_forwarded(oop p); - template inline void satb_barrier(T* field); inline void satb_enqueue(oop value); @@ -122,25 +120,26 @@ public: inline oop oop_xchg(DecoratorSet decorators, T* addr, oop new_value); template - void write_ref_field_post(T* field); + void write_ref_field_post(T* field, oop new_value); void write_ref_array(HeapWord* start, size_t count); private: template void arraycopy_marking(T* dst, size_t count); + + template + bool is_above_tams(const ShenandoahMarkingContext* ctx, T* dst) const; + template inline void arraycopy_evacuation(T* src, size_t count); template inline void arraycopy_update(T* src, size_t count); - inline void clone_evacuation(oop src); - inline void clone_update(oop src); + template + inline void clone_work(oop src); - template - inline void arraycopy_work(T* src, size_t count); - - inline bool need_bulk_update(HeapWord* dst); + inline bool need_bulk_update(HeapWord* dst) const; public: // Callbacks for runtime accesses. template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index e7ddfdb0f6a..af0622693fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -43,18 +43,6 @@ #include "memory/iterator.inline.hpp" #include "oops/oop.inline.hpp" -inline oop ShenandoahBarrierSet::resolve_forwarded_not_null(oop p) { - return ShenandoahForwarding::get_forwardee(p); -} - -inline oop ShenandoahBarrierSet::resolve_forwarded(oop p) { - if (p != nullptr) { - return resolve_forwarded_not_null(p); - } else { - return p; - } -} - template inline oop ShenandoahBarrierSet::load_reference_barrier_mutator(oop obj, T* load_addr) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); @@ -119,7 +107,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj) { if (_heap->has_forwarded_objects() && _heap->in_collection_set(obj)) { // Subsumes null-check assert(obj != nullptr, "cset check must have subsumed null-check"); - oop fwd = resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); if (obj == fwd && _heap->is_evacuation_in_progress()) { Thread* t = Thread::current(); return _heap->evacuate_object(obj, t); @@ -220,23 +208,28 @@ inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oo } template -inline void ShenandoahBarrierSet::write_ref_field_post(T* field) { +inline void ShenandoahBarrierSet::write_ref_field_post(T* field, oop new_value) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + if (new_value == nullptr) { + // Null reference stores do not require card mark. + return; + } + if (_heap->is_in_young(field)) { // Young field stores do not require card mark. return; } - T heap_oop = RawAccess<>::oop_load(field); - if (CompressedOops::is_null(heap_oop)) { - // Null reference store do not require card mark. - return; - } - oop obj = CompressedOops::decode_not_null(heap_oop); - if (!_heap->is_in_young(obj)) { + + if (!_heap->is_in_young(new_value)) { // Not an old->young reference store. return; } + volatile CardTable::CardValue* byte = card_table()->byte_for(field); + if (UseCondCardMark && (*byte == CardTable::dirty_card_val())) { + return; + } *byte = CardTable::dirty_card_val(); } @@ -333,7 +326,7 @@ inline void ShenandoahBarrierSet::AccessBarrier::oop_st oop_store_common(addr, value); if (ShenandoahCardBarrier) { ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, value); } } @@ -359,7 +352,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); oop result = bs->oop_cmpxchg(decorators, addr, compare_value, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -383,7 +376,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato auto addr = AccessInternal::oop_field_addr(base, offset); oop result = bs->oop_cmpxchg(resolved_decorators, addr, compare_value, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -405,7 +398,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); oop result = bs->oop_xchg(decorators, addr, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -429,7 +422,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato auto addr = AccessInternal::oop_field_addr(base, offset); oop result = bs->oop_xchg(resolved_decorators, addr, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -469,18 +462,10 @@ public: virtual void do_oop(narrowOop* p) { do_oop_work(p); } }; -void ShenandoahBarrierSet::clone_evacuation(oop obj) { - assert(_heap->is_evacuation_in_progress(), "only during evacuation"); +template +void ShenandoahBarrierSet::clone_work(oop obj) { if (need_bulk_update(cast_from_oop(obj))) { - ShenandoahUpdateEvacForCloneOopClosure cl; - obj->oop_iterate(&cl); - } -} - -void ShenandoahBarrierSet::clone_update(oop obj) { - assert(_heap->is_update_refs_in_progress(), "only during update-refs"); - if (need_bulk_update(cast_from_oop(obj))) { - ShenandoahUpdateEvacForCloneOopClosure cl; + ShenandoahUpdateEvacForCloneOopClosure cl; obj->oop_iterate(&cl); } } @@ -494,9 +479,9 @@ void ShenandoahBarrierSet::AccessBarrier::clone_in_heap if (gc_state != 0 && ShenandoahCloneBarrier) { ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { - bs->clone_evacuation(src); + bs->clone_work(src); } else if ((gc_state & ShenandoahHeap::UPDATE_REFS) != 0) { - bs->clone_update(src); + bs->clone_work(src); } } @@ -524,40 +509,6 @@ OopCopyResult ShenandoahBarrierSet::AccessBarrier::oop_ return result; } -template -void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { - // Young cycles are allowed to run when old marking is in progress. When old marking is in progress, - // this barrier will be called with ENQUEUE=true and HAS_FWD=false, even though the young generation - // may have forwarded objects. - assert(HAS_FWD == _heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded object status is sane"); - // This function cannot be called to handle marking and evacuation at the same time (they operate on - // different sides of the copy). - static_assert((HAS_FWD || EVAC) != ENQUEUE, "Cannot evacuate and mark both sides of copy."); - - Thread* thread = Thread::current(); - SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); - ShenandoahMarkingContext* ctx = _heap->marking_context(); - const ShenandoahCollectionSet* const cset = _heap->collection_set(); - T* end = src + count; - for (T* elem_ptr = src; elem_ptr < end; elem_ptr++) { - T o = RawAccess<>::oop_load(elem_ptr); - if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); - if (HAS_FWD && cset->is_in(obj)) { - oop fwd = resolve_forwarded_not_null(obj); - if (EVAC && obj == fwd) { - fwd = _heap->evacuate_object(obj, thread); - } - shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); - ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); - } - if (ENQUEUE && !ctx->is_marked_strong(obj)) { - _satb_mark_queue_set.enqueue_known_active(queue, obj); - } - } - } -} - template void ShenandoahBarrierSet::arraycopy_barrier(T* src, T* dst, size_t count) { if (count == 0) { @@ -589,31 +540,92 @@ void ShenandoahBarrierSet::arraycopy_barrier(T* src, T* dst, size_t count) { template void ShenandoahBarrierSet::arraycopy_marking(T* dst, size_t count) { assert(_heap->is_concurrent_mark_in_progress(), "only during marking"); - if (ShenandoahSATBBarrier) { - if (!_heap->marking_context()->allocated_after_mark_start(reinterpret_cast(dst)) || - (IS_GENERATIONAL && _heap->heap_region_containing(dst)->is_old() && _heap->is_concurrent_young_mark_in_progress())) { - arraycopy_work(dst, count); + if (!ShenandoahSATBBarrier) { + return; + } + + const ShenandoahMarkingContext* ctx = _heap->marking_context(); + // Everything allocated above TAMS is alive and doesn't need the barrier to keep it that way + if (is_above_tams(ctx, dst)) { + return; + } + + assert(!_heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded object status is sane"); + Thread* thread = Thread::current(); + SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); + T* end = dst + count; + for (T* elem_ptr = dst; elem_ptr < end; ++elem_ptr) { + T o = RawAccess<>::oop_load(elem_ptr); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (!ctx->is_marked_strong(obj)) { + _satb_mark_queue_set.enqueue_known_active(queue, obj); + } } } } -inline bool ShenandoahBarrierSet::need_bulk_update(HeapWord* ary) { +template +bool ShenandoahBarrierSet::is_above_tams(const ShenandoahMarkingContext* ctx, T* dst) const { + // TAMS for an old region is unreliable during a young-only mark, so overwritten pointers in old dst arrays must + // be enqueued to preserve old->young referents copied in and overwritten after init mark. See JDK-8373116. + return ctx->allocated_after_mark_start(reinterpret_cast(dst)) + && !(IS_GENERATIONAL + && _heap->heap_region_containing(dst)->is_old() + && _heap->is_concurrent_young_mark_in_progress()); +} + +inline bool ShenandoahBarrierSet::need_bulk_update(HeapWord* ary) const { return ary < _heap->heap_region_containing(ary)->get_update_watermark(); } template void ShenandoahBarrierSet::arraycopy_evacuation(T* src, size_t count) { assert(_heap->is_evacuation_in_progress(), "only during evacuation"); - if (need_bulk_update(reinterpret_cast(src))) { - arraycopy_work(src, count); + if (!need_bulk_update(reinterpret_cast(src))) { + return; + } + + assert(_heap->has_forwarded_objects(), "Forwarded object status is sane"); + Thread* thread = Thread::current(); + const ShenandoahCollectionSet* const cset = _heap->collection_set(); + T* end = src + count; + for (T* elem_ptr = src; elem_ptr < end; ++elem_ptr) { + T o = RawAccess<>::oop_load(elem_ptr); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (cset->is_in(obj)) { + oop fwd = ShenandoahForwarding::get_forwardee(obj); + if (obj == fwd) { + fwd = _heap->evacuate_object(obj, thread); + } + shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); + ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); + } + } } } template void ShenandoahBarrierSet::arraycopy_update(T* src, size_t count) { assert(_heap->is_update_refs_in_progress(), "only during update-refs"); - if (need_bulk_update(reinterpret_cast(src))) { - arraycopy_work(src, count); + if (!need_bulk_update(reinterpret_cast(src))) { + return; + } + + assert(_heap->has_forwarded_objects(), "Forwarded object status is sane"); + const ShenandoahCollectionSet* const cset = _heap->collection_set(); + T* end = src + count; + for (T* elem_ptr = src; elem_ptr < end; ++elem_ptr) { + T o = RawAccess<>::oop_load(elem_ptr); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (cset->is_in(obj)) { + oop fwd = ShenandoahForwarding::get_forwardee(obj); + shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); + ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); + } + } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 976a505c713..eb40dfbd31d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -95,14 +95,18 @@ template class ShenandoahMarkRefsClosure : public ShenandoahMarkRefsSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + ALWAYSINLINE + void do_oop_work(T* p) { work(p); } public: ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q) : ShenandoahMarkRefsSuperClosure(q, rp, old_q) {}; - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } + ALWAYSINLINE + void do_oop(narrowOop* p) override { do_oop_work(p); } + + ALWAYSINLINE + void do_oop(oop* p) override { do_oop_work(p); } }; class ShenandoahForwardedIsAliveClosure : public BoolObjectClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 7580b8d1015..f57a9b20957 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -30,6 +30,7 @@ #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" +#include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" @@ -76,7 +77,8 @@ ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToSc _weak(false) {} template -inline void ShenandoahMarkRefsSuperClosure::work(T* p) { +ALWAYSINLINE +void ShenandoahMarkRefsSuperClosure::work(T* p) { ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, _weak); } @@ -87,7 +89,7 @@ bool ShenandoahForwardedIsAliveClosure::do_object_b(oop obj) { if (CompressedOops::is_null(obj)) { return false; } - obj = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + obj = ShenandoahForwarding::get_forwardee(obj); shenandoah_assert_not_forwarded_if(nullptr, obj, ShenandoahHeap::heap()->is_concurrent_mark_in_progress()); return _mark_context->is_marked_or_old(obj); } @@ -142,7 +144,7 @@ void ShenandoahEvacuateUpdateRootClosureBase::do_oop( template template void ShenandoahEvacuateUpdateRootClosureBase::do_oop_work(T* p) { - assert(_heap->is_concurrent_weak_root_in_progress() || + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || _heap->is_concurrent_strong_root_in_progress(), "Only do this in root processing phase"); @@ -152,7 +154,7 @@ void ShenandoahEvacuateUpdateRootClosureBase::do_oop_ if (_heap->in_collection_set(obj)) { assert(_heap->is_evacuation_in_progress(), "Only do this when evacuation is in progress"); shenandoah_assert_marked(p, obj); - oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop resolved = ShenandoahForwarding::get_forwardee(obj); if (resolved == obj) { Thread* thr = STABLE_THREAD ? _thread : Thread::current(); assert(thr == Thread::current(), "Wrong thread"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 3116ec30665..d1c25eb49b4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -111,7 +111,7 @@ public: return; } - { + if (_heap->is_evacuation_in_progress()) { ShenandoahNMethodLocker locker(nm_data->lock()); // Heal oops diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 08032b224d0..18f8f5a4142 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.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. * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -32,6 +32,7 @@ #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" +#include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" @@ -108,6 +109,7 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare); EventMark em("%s", msg); + heap->try_inject_pin(); // Evacuation is complete, retire gc labs and change gc state heap->concurrent_prepare_for_update_refs(); } @@ -124,6 +126,7 @@ void ShenandoahConcurrentGC::entry_update_card_table() { ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), "concurrent update cards"); + heap->try_inject_pin(); // 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"); @@ -182,7 +185,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { assert(heap->is_concurrent_weak_root_in_progress(), "Must be doing weak roots now"); - // Concurrent stack processing + // Finish all thread/stack roots if needed. This completes stack watermark processing. if (heap->is_evacuation_in_progress()) { entry_thread_roots(); } @@ -203,8 +206,6 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // we will not age young-gen objects in the case that we skip evacuation. entry_cleanup_early(); - heap->free_set()->log_status_under_lock(); - // Processing strong roots // This may be skipped if there is nothing to update/evacuate. // If so, strong_root_in_progress would be unset. @@ -212,6 +213,9 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { entry_strong_roots(); } + // Roots processing is complete, put the weak roots flag down. + entry_final_roots(); + // Continue the cycle with evacuation and optional update-refs. // This may be skipped if there is nothing to evacuate. // If so, evac_in_progress would be unset by collection set preparation code. @@ -250,9 +254,18 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { entry_cleanup_complete(); } else { _abbreviated = true; - if (!entry_final_roots()) { - assert(_degen_point != _degenerated_unset, "Need to know where to start degenerated cycle"); - return false; + + if (heap->mode()->is_generational()) { + entry_complete_abbreviated_cycle(); + + // If the promote-in-place operation was cancelled, we can have the degenerated + // cycle complete the operation. It will see that no evacuations are in progress, + // and that there are regions wanting promotion. The risk with not handling the + // cancellation would be failing to restore top for these regions and leaving + // them unable to serve allocations for the old generation. + if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { + return false; + } } // In normal cycle, final-update-refs would verify at the end of the cycle. @@ -276,34 +289,35 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { return true; } -bool ShenandoahConcurrentGC::complete_abbreviated_cycle() { +void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() { shenandoah_assert_generational(); ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + static const char* msg = "Concurrent complete abbreviated cycle"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), + msg); + + heap->try_inject_pin(); // We chose not to evacuate because we found sufficient immediate garbage. // However, there may still be regions to promote in place, so do that now. if (heap->old_generation()->has_in_place_promotions()) { - entry_promote_in_place(); - - // If the promote-in-place operation was cancelled, we can have the degenerated - // cycle complete the operation. It will see that no evacuations are in progress, - // and that there are regions wanting promotion. The risk with not handling the - // cancellation would be failing to restore top for these regions and leaving - // them unable to serve allocations for the old generation.This will leave the weak - // roots flag set (the degenerated cycle will unset it). - if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { - return false; - } + ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::complete_abbreviated_promote_in_place); + ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::complete_abbreviated_promote_in_place); + heap->promote_regions_in_place(_generation, true); } // At this point, the cycle is effectively complete. If the cycle has been cancelled here, // the control thread will detect it on its next iteration and run a degenerated young cycle. - if (!_generation->is_old()) { + if (!heap->cancelled_gc() && !_generation->is_old()) { + ShenandoahTimingsTracker tracker(ShenandoahPhaseTimings::complete_abbreviated_update_region_ages); heap->update_region_ages(_generation->complete_marking_context()); } - - return true; } void ShenandoahConcurrentGC::vmop_entry_init_mark() { @@ -324,6 +338,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_mark() { heap->try_inject_alloc_failure(); VM_ShenandoahFinalMarkStartEvac op(this); VMThread::execute(&op); // jump to entry_final_mark under safepoint + heap->try_inject_pin(); } void ShenandoahConcurrentGC::vmop_entry_init_update_refs() { @@ -332,6 +347,7 @@ void ShenandoahConcurrentGC::vmop_entry_init_update_refs() { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::init_update_refs_gross); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); VM_ShenandoahInitUpdateRefs op(this); VMThread::execute(&op); } @@ -342,6 +358,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_update_refs() { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_update_refs_gross); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); VM_ShenandoahFinalUpdateRefs op(this); VMThread::execute(&op); } @@ -353,6 +370,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_verify() { // This phase does not use workers, no need for setup heap->try_inject_alloc_failure(); + heap->try_inject_pin(); VM_ShenandoahFinalVerify op(this); VMThread::execute(&op); } @@ -412,6 +430,7 @@ void ShenandoahConcurrentGC::entry_final_verify() { void ShenandoahConcurrentGC::entry_reset() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + heap->release_injected_pins(); heap->try_inject_alloc_failure(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); @@ -472,6 +491,7 @@ void ShenandoahConcurrentGC::entry_mark() { heap->try_inject_alloc_failure(); op_mark(); + heap->try_inject_pin(); } void ShenandoahConcurrentGC::entry_thread_roots() { @@ -485,6 +505,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() { msg); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_thread_roots(); } @@ -499,6 +520,7 @@ void ShenandoahConcurrentGC::entry_weak_refs() { "concurrent weak references"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_weak_refs(); } @@ -514,6 +536,7 @@ void ShenandoahConcurrentGC::entry_weak_roots() { "concurrent weak root"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_weak_roots(); } @@ -529,6 +552,7 @@ void ShenandoahConcurrentGC::entry_class_unloading() { "concurrent class unloading"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_class_unloading(); } @@ -546,6 +570,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() { "concurrent strong root"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_strong_roots(); } @@ -558,6 +583,7 @@ void ShenandoahConcurrentGC::entry_cleanup_early() { // This phase does not use workers, no need for setup heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_cleanup_early(); if (!heap->is_evacuation_in_progress()) { // This is an abbreviated cycle. Rebuild the freeset in order to establish reserves for the next GC cycle. Doing @@ -580,19 +606,10 @@ void ShenandoahConcurrentGC::entry_evacuate() { "concurrent evacuation"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_evacuate(); } -void ShenandoahConcurrentGC::entry_promote_in_place() const { - shenandoah_assert_generational(); - - ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::promote_in_place); - ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::promote_in_place); - EventMark em("%s", "Promote in place"); - - ShenandoahGenerationalHeap::heap()->promote_regions_in_place(_generation, true); -} - void ShenandoahConcurrentGC::entry_update_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); @@ -603,6 +620,7 @@ void ShenandoahConcurrentGC::entry_update_thread_roots() { // No workers used in this phase, no setup required heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_update_thread_roots(); } @@ -618,6 +636,7 @@ void ShenandoahConcurrentGC::entry_update_refs() { "concurrent reference update"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_update_refs(); } @@ -910,7 +929,7 @@ void ShenandoahEvacUpdateCleanupOopStorageRootsClosure::do_oop(oop* p) { ShenandoahHeap::atomic_clear_oop(p, obj); } } else if (_evac_in_progress && _heap->in_collection_set(obj)) { - oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop resolved = ShenandoahForwarding::get_forwardee(obj); if (resolved == obj) { resolved = _heap->evacuate_object(obj, _thread); } @@ -1228,26 +1247,14 @@ void ShenandoahConcurrentGC::op_final_update_refs() { } } -bool ShenandoahConcurrentGC::entry_final_roots() { +void ShenandoahConcurrentGC::entry_final_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - - const char* msg = conc_final_roots_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); EventMark em("%s", msg); - ShenandoahWorkerScope scope(heap->workers(), - ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), - msg); - - if (heap->mode()->is_generational()) { - if (!complete_abbreviated_cycle()) { - return false; - } - } heap->concurrent_final_roots(); - return true; } void ShenandoahConcurrentGC::op_verify_final() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index fde585b4aa9..e763d1853e3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -91,6 +91,8 @@ protected: void entry_class_unloading(); void entry_strong_roots(); void entry_cleanup_early(); + void entry_complete_abbreviated_cycle(); + void entry_final_roots(); void entry_evacuate(); void entry_update_thread_roots(); void entry_update_card_table(); @@ -98,12 +100,6 @@ protected: void entry_update_refs(); void entry_cleanup_complete(); - // This is the last phase of a cycle which performs no evacuations - bool entry_final_roots(); - - // Called when the collection set is empty, but the generational mode has regions to promote in place - void entry_promote_in_place() const; - // Actual work for the phases void op_reset(); void op_init_mark(); @@ -135,8 +131,6 @@ protected: private: void start_mark(); - bool complete_abbreviated_cycle(); - static bool has_in_place_promotions(ShenandoahHeap* heap); // Messages for GC trace events, they have to be immortal for diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index f2447db8210..3fefae58ef8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -221,22 +221,22 @@ void ShenandoahControlThread::run_service() { // Wait before performing the next action. If allocation happened during this wait, // we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle, // back off exponentially. - const double before_sleep = most_recent_wake_time; if (heap->has_changed()) { sleep = ShenandoahControlIntervalMin; - } else if ((before_sleep - last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ + } else if ((most_recent_wake_time - last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ sleep = MIN2(ShenandoahControlIntervalMax, MAX2(1, sleep * 2)); - last_sleep_adjust_time = before_sleep; + last_sleep_adjust_time = most_recent_wake_time; } MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); + const double before_sleep_time = os::elapsedTime(); ml.wait(sleep); + most_recent_wake_time = os::elapsedTime(); // Record a conservative estimate of the longest anticipated sleep duration until we sample again. double planned_sleep_interval = MIN2(ShenandoahControlIntervalMax, MAX2(1, sleep * 2)) / 1000.0; - most_recent_wake_time = os::elapsedTime(); heuristics->update_should_start_query_times(most_recent_wake_time, planned_sleep_interval); if (LogTarget(Debug, gc, thread)::is_enabled()) { - double elapsed = most_recent_wake_time - before_sleep; - double hiccup = elapsed - double(sleep); + double elapsed = most_recent_wake_time - before_sleep_time; + double hiccup = elapsed - double(sleep) / 1000.0; if (hiccup > 0.001) { log_debug(gc, thread)("Control Thread hiccup time: %.3fs", hiccup); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index b2d578c599d..3c3cdc4a90a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -45,19 +45,19 @@ #include "runtime/vmThread.hpp" #include "utilities/events.hpp" -ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation) : +ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation, bool do_old_gc_bootstrap) : ShenandoahGC(generation), _degen_point(degen_point), - _abbreviated(false) { + _abbreviated(false), + _do_old_gc_bootstrap(do_old_gc_bootstrap) { } bool ShenandoahDegenGC::collect(GCCause::Cause cause) { vmop_degenerated(); ShenandoahHeap* heap = ShenandoahHeap::heap(); if (heap->mode()->is_generational()) { - bool is_bootstrap_gc = heap->young_generation()->is_bootstrap_cycle(); FormatBuffer<32> buf("Degenerated %s GC", _generation->name()); - const char* msg = is_bootstrap_gc ? "Degenerated Bootstrap Old GC" : buf.buffer(); + const char* msg = _do_old_gc_bootstrap ? "Degenerated Bootstrap Old GC" : buf.buffer(); heap->mmu_tracker()->record_degenerated(GCId::current(), msg); heap->log_heap_status(FormatBuffer<64>("At end of %s", msg)); } @@ -91,6 +91,7 @@ void ShenandoahDegenGC::entry_degenerated() { void ShenandoahDegenGC::op_degenerated() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + heap->release_injected_pins(); // Degenerated GC is STW, but it can also fail. Current mechanics communicates // GC failure via cancelled_concgc() flag. So, if we detect the failure after // some phase, we have to upgrade the Degenerate GC to Full GC. @@ -331,13 +332,13 @@ void ShenandoahDegenGC::op_degenerated() { policy->record_degenerated(_generation->is_young(), _abbreviated, progress); if (progress) { heap->notify_gc_progress(); - _generation->heuristics()->record_degenerated(); + _generation->heuristics()->record_degenerated(heap->mode()->is_generational() && _generation->is_global()); heap->start_idle_span(); } else if (policy->should_upgrade_degenerated_gc()) { // Upgrade to full GC, register full-GC impact on heuristics. op_degenerated_futile(); } else { - _generation->heuristics()->record_degenerated(); + _generation->heuristics()->record_degenerated(heap->mode()->is_generational() && _generation->is_global()); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp index 34b9688106c..f3d033af128 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp @@ -35,9 +35,10 @@ class ShenandoahDegenGC : public ShenandoahGC { private: const ShenandoahDegenPoint _degen_point; bool _abbreviated; + const bool _do_old_gc_bootstrap; public: - ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation); + ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation, bool do_old_gc_bootstrap = false); bool collect(GCCause::Cause cause) override; private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 6563ffb6359..9db0f662705 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2016, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * 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 @@ -26,6 +26,7 @@ #include "gc/shared/tlab_globals.hpp" #include "gc/shenandoah/shenandoahAffiliation.hpp" +#include "gc/shenandoah/shenandoahAllocator.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" @@ -35,7 +36,6 @@ #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" -#include "runtime/orderAccess.hpp" static const char* partition_name(ShenandoahFreeSetPartitionId t) { switch (t) { @@ -222,18 +222,18 @@ ShenandoahFreeSetPartitionId ShenandoahFreeSet::prepare_to_promote_in_place(size return p; } -inline bool ShenandoahFreeSet::can_allocate_from(ShenandoahHeapRegion *r) const { +bool ShenandoahFreeSet::can_allocate_from(ShenandoahHeapRegion *r) const { const auto state = r->state(); return ShenandoahHeapRegion::is_empty_state(state) || (ShenandoahHeapRegion::is_trash(state) && !_heap->is_concurrent_weak_root_in_progress()); } -inline bool ShenandoahFreeSet::can_allocate_from(size_t idx) const { +bool ShenandoahFreeSet::can_allocate_from(size_t idx) const { ShenandoahHeapRegion* r = _heap->get_region(idx); return can_allocate_from(r); } -inline size_t ShenandoahFreeSet::alloc_capacity(ShenandoahHeapRegion *r) const { +size_t ShenandoahFreeSet::alloc_capacity(ShenandoahHeapRegion *r) const { if (r->is_trash()) { // This would be recycled on allocation path return ShenandoahHeapRegion::region_size_bytes(); @@ -242,15 +242,11 @@ inline size_t ShenandoahFreeSet::alloc_capacity(ShenandoahHeapRegion *r) const { } } -inline size_t ShenandoahFreeSet::alloc_capacity(size_t idx) const { +size_t ShenandoahFreeSet::alloc_capacity(size_t idx) const { ShenandoahHeapRegion* r = _heap->get_region(idx); return alloc_capacity(r); } -inline bool ShenandoahFreeSet::has_alloc_capacity(ShenandoahHeapRegion *r) const { - return alloc_capacity(r) > 0; -} - // This is used for unit testing. Do not use in production code. void ShenandoahFreeSet::resize_old_collector_capacity(size_t regions) { shenandoah_assert_heaplocked(); @@ -289,7 +285,179 @@ void ShenandoahFreeSet::resize_old_collector_capacity(size_t regions) { // else, old generation is already appropriately sized } -inline idx_t ShenandoahRegionPartitions::leftmost(ShenandoahFreeSetPartitionId which_partition) const { +void ShenandoahFreeSet::notify_allocation(ShenandoahFreeSetPartitionId partition, bool in_new_region, bool boundary_changed) { + switch (partition) { + case ShenandoahFreeSetPartitionId::Mutator: + recompute_total_used(); + if (in_new_region) { + recompute_total_affiliated(); + } + break; + case ShenandoahFreeSetPartitionId::Collector: + recompute_total_used(); + if (in_new_region) { + recompute_total_affiliated(); + } + break; + case ShenandoahFreeSetPartitionId::OldCollector: + recompute_total_used(); + if (in_new_region) { + recompute_total_affiliated(); + } + break; + case ShenandoahFreeSetPartitionId::NotFree: + default: + assert(false, "won't happen"); + } + if (boundary_changed) { + _partitions.assert_bounds(); + } else { + _partitions.assert_bounds_sanity(); + } +} + +void ShenandoahFreeSet::increase_partition_used(ShenandoahFreeSetPartitionId partition, size_t bytes) { + _partitions.increase_used(partition, bytes); +} + +void ShenandoahFreeSet::mark_region_used(ShenandoahFreeSetPartitionId partition) { + _partitions.one_region_is_no_longer_empty(partition); +} + +size_t ShenandoahFreeSet::retire_region(ShenandoahFreeSetPartitionId partition, size_t idx, size_t used_bytes) { + return _partitions.retire_from_partition(partition, idx, used_bytes); +} + +template +ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t min_size_words, bool& in_new_region) { + shenandoah_assert_heaplocked(); + // Allocation bias is a Mutator-only heuristic; updating it for collector allocations would + // perturb mutator placement and burn down the shared bias weight. + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::Mutator) { + update_allocation_bias(); + } + + if (_partitions.is_empty(PARTITION)) { + return nullptr; + } + + constexpr ShenandoahAffiliation affiliation = + (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) ? OLD_GENERATION : YOUNG_GENERATION; + + ShenandoahHeapRegion* result = nullptr; + ShenandoahHeapRegion* free_region = nullptr; + size_t min_size_bytes = min_size_words * HeapWordSize; + + auto search = [&](auto& iterator) { + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + if (_heap->is_concurrent_weak_root_in_progress() && r->is_trash()) continue; + r->try_recycle_under_lock(); + if (r->is_empty()) { + if (free_region == nullptr) free_region = r; + // Mutator takes the first region with capacity. Collectors prefer an affiliated region + // and only fall back to an empty region (below) to preserve free regions. + if (PARTITION == ShenandoahFreeSetPartitionId::Mutator && alloc_capacity(r) >= min_size_bytes) { + result = r; + return; + } + } else if (r->affiliation() == affiliation) { + if (alloc_capacity(r) >= min_size_bytes) { result = r; return; } + } + } + }; + + if (_partitions.alloc_from_left_bias(PARTITION)) { + ShenandoahLeftRightIterator iterator(&_partitions, PARTITION); + search(iterator); + } else { + ShenandoahRightLeftIterator iterator(&_partitions, PARTITION); + search(iterator); + } + + // For collector partitions: fall back to any free (empty) region if no affiliated region found. + if constexpr (PARTITION != ShenandoahFreeSetPartitionId::Mutator) { + if (result == nullptr && free_region != nullptr) { + result = free_region; + } + } + + if (result == nullptr) { + return nullptr; + } + + // Prepare the region for allocation. + in_new_region = result->is_empty(); + if (in_new_region) { + assert(!result->is_affiliated(), "New region should be unaffiliated"); + result->set_affiliation(affiliation); + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) { + result->end_preemptible_coalesce_and_fill(); + } + assert(_heap->marking_context()->top_at_mark_start(result) == result->bottom(), + "Newly established allocation region (%zu) must start with TAMS equal to bottom", result->index()); + shenandoah_assert_clear_above_top(result); + } + return result; +} + +// Explicit instantiations for find_region_for_alloc. +template ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t, bool&); +template ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t, bool&); +template ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t, bool&); + +ShenandoahHeapRegion* ShenandoahFreeSet::steal_from_mutator(ShenandoahFreeSetPartitionId target_partition, + ShenandoahAllocRequest& req) { + shenandoah_assert_heaplocked(); + assert(target_partition != ShenandoahFreeSetPartitionId::Mutator, "Cannot steal from self"); + + if (_partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Mutator) == 0) { + return nullptr; + } + + ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator, true); + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + if (can_allocate_from(r)) { + if (req.is_old()) { + if (!flip_to_old_gc(r)) { + continue; + } + } else { + flip_to_gc(r); + } + log_debug(gc, free)("Flipped region %zu to gc for request: " PTR_FORMAT, idx, p2i(&req)); + + r->try_recycle_under_lock(); + assert(r->is_empty(), "Must be empty"); + ShenandoahAffiliation aff = (target_partition == ShenandoahFreeSetPartitionId::OldCollector) + ? OLD_GENERATION : YOUNG_GENERATION; + r->set_affiliation(aff); + if (r->is_old()) { + r->end_preemptible_coalesce_and_fill(); + } + return r; + } + } + return nullptr; +} + +idx_t ShenandoahRegionPartitions::leftmost(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free partition must be valid"); idx_t idx = _leftmosts[int(which_partition)]; if (idx >= _max) { @@ -302,7 +470,7 @@ inline idx_t ShenandoahRegionPartitions::leftmost(ShenandoahFreeSetPartitionId w } } -inline idx_t ShenandoahRegionPartitions::rightmost(ShenandoahFreeSetPartitionId which_partition) const { +idx_t ShenandoahRegionPartitions::rightmost(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free partition must be valid"); idx_t idx = _rightmosts[int(which_partition)]; // Cannot assert that membership[which_partition.is_set(idx) because this helper method may be used @@ -793,12 +961,12 @@ inline bool ShenandoahRegionPartitions::partition_id_matches(idx_t idx, Shenando } #endif -inline bool ShenandoahRegionPartitions::is_empty(ShenandoahFreeSetPartitionId which_partition) const { +bool ShenandoahRegionPartitions::is_empty(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free partition must be valid"); return (leftmost(which_partition) > rightmost(which_partition)); } -inline idx_t ShenandoahRegionPartitions::find_index_of_next_available_region( +idx_t ShenandoahRegionPartitions::find_index_of_next_available_region( ShenandoahFreeSetPartitionId which_partition, idx_t start_index) const { idx_t rightmost_idx = rightmost(which_partition); idx_t leftmost_idx = leftmost(which_partition); @@ -814,7 +982,7 @@ inline idx_t ShenandoahRegionPartitions::find_index_of_next_available_region( return result; } -inline idx_t ShenandoahRegionPartitions::find_index_of_previous_available_region( +idx_t ShenandoahRegionPartitions::find_index_of_previous_available_region( ShenandoahFreeSetPartitionId which_partition, idx_t last_index) const { idx_t rightmost_idx = rightmost(which_partition); idx_t leftmost_idx = leftmost(which_partition); @@ -1187,7 +1355,7 @@ void ShenandoahRegionPartitions::assert_bounds() { "Mutator humongous waste must match"); } -inline void ShenandoahRegionPartitions::assert_bounds_sanity() { +void ShenandoahRegionPartitions::assert_bounds_sanity() { for (uint8_t i = 0; i < UIntNumPartitions; i++) { ShenandoahFreeSetPartitionId partition = static_cast(i); assert(leftmost(partition) == _max || membership(leftmost(partition)) == partition, "Left most boundry must be sane"); @@ -1307,81 +1475,6 @@ void ShenandoahFreeSet::add_promoted_in_place_region_to_old_collector(Shenandoah _partitions.assert_bounds(); } -template -HeapWord* ShenandoahFreeSet::allocate_with_affiliation(Iter& iterator, - ShenandoahAffiliation affiliation, - ShenandoahAllocRequest& req, - bool& in_new_region) { - assert(affiliation != ShenandoahAffiliation::FREE, "Must not"); - ShenandoahHeapRegion* free_region = nullptr; - for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { - ShenandoahHeapRegion* r = _heap->get_region(idx); - if (r->affiliation() == affiliation) { - HeapWord* result = try_allocate_in(r, req, in_new_region); - if (result != nullptr) { - return result; - } - } else if (free_region == nullptr && r->affiliation() == FREE) { - free_region = r; - } - } - // Failed to allocate within any affiliated region, try the first free region in the partition. - if (free_region != nullptr) { - HeapWord* result = try_allocate_in(free_region, req, in_new_region); - assert(result != nullptr, "Allocate in free region in the partition always succeed."); - return result; - } - log_debug(gc, free)("Could not allocate collector region with affiliation: %s for request " PTR_FORMAT, - shenandoah_affiliation_name(affiliation), p2i(&req)); - return nullptr; -} - -HeapWord* ShenandoahFreeSet::allocate_single(ShenandoahAllocRequest& req, bool& in_new_region) { - shenandoah_assert_heaplocked(); - - // Scan the bitmap looking for a first fit. - // - // Leftmost and rightmost bounds provide enough caching to walk bitmap efficiently. Normally, - // we would find the region to allocate at right away. - // - // Allocations are biased: GC allocations are taken from the high end of the heap. Regular (and TLAB) - // mutator allocations are taken from the middle of heap, below the memory reserved for Collector. - // Humongous mutator allocations are taken from the bottom of the heap. - // - // Free set maintains mutator and collector partitions. Normally, each allocates only from its partition, - // except in special cases when the collector steals regions from the mutator partition. - - // Overwrite with non-zero (non-null) values only if necessary for allocation bookkeeping. - - if (req.is_mutator_alloc()) { - return allocate_for_mutator(req, in_new_region); - } else { - return allocate_for_collector(req, in_new_region); - } -} - -HeapWord* ShenandoahFreeSet::allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region) { - update_allocation_bias(); - - if (_partitions.is_empty(ShenandoahFreeSetPartitionId::Mutator)) { - // There is no recovery. Mutator does not touch collector view at all. - return nullptr; - } - - // Try to allocate in the mutator view - if (_partitions.alloc_from_left_bias(ShenandoahFreeSetPartitionId::Mutator)) { - // Allocate from low to high memory. This keeps the range of fully empty regions more tightly packed. - // Note that the most recently allocated regions tend not to be evacuated in a given GC cycle. So this - // tends to accumulate "fragmented" uncollected regions in high memory. - ShenandoahLeftRightIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); - return allocate_from_regions(iterator, req, in_new_region); - } - - // Allocate from high to low memory. This preserves low memory for humongous allocations. - ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); - return allocate_from_regions(iterator, req, in_new_region); -} - void ShenandoahFreeSet::update_allocation_bias() { if (_alloc_bias_weight-- <= 0) { // We have observed that regions not collected in previous GC cycle tend to congregate at one end or the other @@ -1408,243 +1501,6 @@ void ShenandoahFreeSet::update_allocation_bias() { } } -template -HeapWord* ShenandoahFreeSet::allocate_from_regions(Iter& iterator, ShenandoahAllocRequest &req, bool &in_new_region) { - for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { - ShenandoahHeapRegion* r = _heap->get_region(idx); - size_t min_size = req.is_lab_alloc() ? req.min_size() : req.size(); - if (alloc_capacity(r) >= min_size * HeapWordSize) { - HeapWord* result = try_allocate_in(r, req, in_new_region); - if (result != nullptr) { - return result; - } - } - } - return nullptr; -} - -HeapWord* ShenandoahFreeSet::allocate_for_collector(ShenandoahAllocRequest &req, bool &in_new_region) { - shenandoah_assert_heaplocked(); - ShenandoahFreeSetPartitionId which_partition = req.is_old()? ShenandoahFreeSetPartitionId::OldCollector: ShenandoahFreeSetPartitionId::Collector; - HeapWord* result = nullptr; - if (_partitions.alloc_from_left_bias(which_partition)) { - ShenandoahLeftRightIterator iterator(&_partitions, which_partition); - result = allocate_with_affiliation(iterator, req.affiliation(), req, in_new_region); - } else { - ShenandoahRightLeftIterator iterator(&_partitions, which_partition); - result = allocate_with_affiliation(iterator, req.affiliation(), req, in_new_region); - } - - if (result != nullptr) { - return result; - } - - // No dice. Can we borrow space from mutator view? - if (!ShenandoahEvacReserveOverflow) { - return nullptr; - } - - if (_partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Mutator) > 0) { - // Try to steal an empty region from the mutator view. - result = try_allocate_from_mutator(req, in_new_region); - } - - // This is it. Do not try to mix mutator and GC allocations, because adjusting region UWM - // due to GC allocations would expose unparsable mutator allocations. - return result; -} - -HeapWord* ShenandoahFreeSet::try_allocate_from_mutator(ShenandoahAllocRequest& req, bool& in_new_region) { - // The collector prefers to keep longer lived regions toward the right side of the heap, so it always - // searches for regions from right to left here. - ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator, true); - for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { - ShenandoahHeapRegion* r = _heap->get_region(idx); - if (can_allocate_from(r)) { - if (req.is_old()) { - if (!flip_to_old_gc(r)) { - continue; - } - } else { - flip_to_gc(r); - } - // Region r is entirely empty. If try_allocate_in fails on region r, something else is really wrong. - // Don't bother to retry with other regions. - log_debug(gc, free)("Flipped region %zu to gc for request: " PTR_FORMAT, idx, p2i(&req)); - return try_allocate_in(r, req, in_new_region); - } - } - - return nullptr; -} - - -HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& in_new_region) { - assert (has_alloc_capacity(r), "Performance: should avoid full regions on this path: %zu", r->index()); - if (_heap->is_concurrent_weak_root_in_progress() && r->is_trash()) { - // We cannot use this region for allocation when weak roots are in progress because the collector may need - // to reference unmarked oops during concurrent classunloading. The collector also needs accurate marking - // information to determine which weak handles need to be null'd out. If the region is recycled before weak - // roots processing has finished, weak root processing may fail to null out a handle into a trashed region. - // This turns the handle into a dangling pointer and will crash or corrupt the heap. - return nullptr; - } - HeapWord* result = nullptr; - // 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.affiliation() below - r->try_recycle_under_lock(); - in_new_region = r->is_empty(); - if (in_new_region) { - log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").", - r->index(), req.type_string(), p2i(&req)); - assert(!r->is_affiliated(), "New region %zu should be unaffiliated", r->index()); - r->set_affiliation(req.affiliation()); - if (r->is_old()) { - // Any OLD region allocated during concurrent coalesce-and-fill does not need to be coalesced and filled because - // all objects allocated within this region are above TAMS (and thus are implicitly marked). In case this is an - // OLD region and concurrent preparation for mixed evacuations visits this region before the start of the next - // old-gen concurrent mark (i.e. this region is allocated following the start of old-gen concurrent mark but before - // 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(); - } -#ifdef ASSERT - ShenandoahMarkingContext* const ctx = _heap->marking_context(); - assert(ctx->top_at_mark_start(r) == r->bottom(), "Newly established allocation region starts with TAMS equal to bottom"); - assert(ctx->is_bitmap_range_within_region_clear(ctx->top_bitmap(r), r->end()), "Bitmap above top_bitmap() must be clear"); -#endif - log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").", - r->index(), req.type_string(), p2i(&req)); - } else { - assert(r->is_affiliated(), "Region %zu that is not new should be affiliated", r->index()); - if (r->affiliation() != req.affiliation()) { - assert(_heap->mode()->is_generational(), "Request for %s from %s region should only happen in generational mode.", - req.affiliation_name(), r->affiliation_name()); - return nullptr; - } - } - - // req.size() is in words, r->free() is in bytes. - if (req.is_lab_alloc()) { - size_t adjusted_size = req.size(); - size_t free = align_down(r->free() >> LogHeapWordSize, MinObjAlignment); - if (adjusted_size > free) { - adjusted_size = free; - } - if (adjusted_size >= req.min_size()) { - result = r->allocate(adjusted_size, req); - assert (result != nullptr, "Allocation must succeed: free %zu, actual %zu", free, adjusted_size); - req.set_actual_size(adjusted_size); - } else { - log_trace(gc, free)("Failed to shrink LAB request (%zu) in region %zu to %zu" - " because min_size() is %zu", req.size(), r->index(), adjusted_size, req.min_size()); - } - } else { - size_t size = req.size(); - result = r->allocate(size, req); - if (result != nullptr) { - // Record actual allocation size - req.set_actual_size(size); - } - } - - if (result != nullptr) { - // Allocation successful, bump stats: - if (req.is_mutator_alloc()) { - assert(req.is_young(), "Mutator allocations always come from young generation."); - _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, req.actual_size() * HeapWordSize); - } else { - assert(req.is_gc_alloc(), "Should be gc_alloc since req wasn't mutator alloc"); - - // For GC allocations, we advance update_watermark because the objects relocated into this memory during - // evacuation are not updated during evacuation. For both young and old regions r, it is essential that all - // PLABs be made parsable at the end of evacuation. This is enabled by retiring all plabs at end of evacuation. - r->set_update_watermark(r->top()); - if (r->is_old()) { - _partitions.increase_used(ShenandoahFreeSetPartitionId::OldCollector, (req.actual_size() + req.waste()) * HeapWordSize); - } else { - _partitions.increase_used(ShenandoahFreeSetPartitionId::Collector, (req.actual_size() + req.waste()) * HeapWordSize); - } - } - } - - ShenandoahFreeSetPartitionId orig_partition; - if (req.is_mutator_alloc()) { - orig_partition = ShenandoahFreeSetPartitionId::Mutator; - } else if (req.is_old()) { - orig_partition = ShenandoahFreeSetPartitionId::OldCollector; - } else { - // Not old collector alloc, so this is a young collector gclab or shared allocation - orig_partition = ShenandoahFreeSetPartitionId::Collector; - } - DEBUG_ONLY(bool boundary_changed = false;) - if ((result != nullptr) && in_new_region) { - _partitions.one_region_is_no_longer_empty(orig_partition); - DEBUG_ONLY(boundary_changed = true;) - } - - if (alloc_capacity(r) < PLAB::min_size() * HeapWordSize) { - // Regardless of whether this allocation succeeded, if the remaining memory is less than PLAB:min_size(), retire this region. - // Note that retire_from_partition() increases used to account for waste. - - size_t idx = r->index(); - size_t waste_bytes = _partitions.retire_from_partition(orig_partition, idx, r->used()); - DEBUG_ONLY(boundary_changed = true;) - if (req.is_mutator_alloc() && (waste_bytes > 0)) { - req.set_waste(waste_bytes / HeapWordSize); - } - } - - switch (orig_partition) { - case ShenandoahFreeSetPartitionId::Mutator: - recompute_total_used(); - if (in_new_region) { - recompute_total_affiliated(); - } - break; - case ShenandoahFreeSetPartitionId::Collector: - recompute_total_used(); - if (in_new_region) { - recompute_total_affiliated(); - } - break; - case ShenandoahFreeSetPartitionId::OldCollector: - recompute_total_used(); - if (in_new_region) { - recompute_total_affiliated(); - } - break; - case ShenandoahFreeSetPartitionId::NotFree: - default: - assert(false, "won't happen"); - } -#ifdef ASSERT - if (boundary_changed) { - _partitions.assert_bounds(); - } else { - _partitions.assert_bounds_sanity(); - } -#endif - return result; -} - HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous) { assert(req.is_mutator_alloc(), "All contiguous allocations are performed by mutator"); shenandoah_assert_heaplocked(); @@ -1915,8 +1771,8 @@ void ShenandoahFreeSet::flip_to_gc(ShenandoahHeapRegion* r) { /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); _partitions.assert_bounds(); - // We do not ensure that the region is no longer trash, relying on try_allocate_in(), which always comes next, - // to recycle trash before attempting to allocate anything in the region. + // We do not ensure that the region is no longer trash, relying on the caller, which always recycles + // trash before attempting to allocate anything in the region. } void ShenandoahFreeSet::clear() { @@ -2487,6 +2343,9 @@ void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_trashed_regions, size_t shenandoah_assert_heaplocked(); assert(rebuild_lock() != nullptr, "sanity"); rebuild_lock()->lock(false); + // Drop cached alloc regions before clearing partition state — partition membership + // is about to change and would invalidate the cached regions. + _heap->allocator()->release_alloc_regions(); // This resets all state information, removing all regions from all sets. clear(); log_debug(gc, free)("Rebuilding FreeSet"); @@ -2512,9 +2371,7 @@ void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_ // Move some of the mutator regions into the Collector and OldCollector partitions in order to satisfy // young_reserve and old_reserve. - size_t young_used_regions, old_used_regions, young_used_bytes, old_used_bytes; - reserve_regions(young_reserve, old_reserve, old_region_count, young_used_regions, old_used_regions, - young_used_bytes, old_used_bytes); + reserve_regions(young_reserve, old_reserve, old_region_count); _total_young_regions = _heap->num_regions() - old_region_count; _total_global_regions = _heap->num_regions(); establish_old_collector_alloc_bias(); @@ -2658,17 +2515,10 @@ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regi // is at least to_reserve_old. // // Returns total mutator alloc capacity, in words. -size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old, size_t &old_region_count, - size_t &young_used_regions, size_t &old_used_regions, - size_t &young_used_bytes, size_t &old_used_bytes) { +size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old, size_t &old_region_count) { const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); size_t mutator_allocatable_words = _prepare_to_rebuild_mutator_free; - young_used_regions = 0; - old_used_regions = 0; - young_used_bytes = 0; - old_used_bytes = 0; - idx_t mutator_low_idx = _partitions.max(); idx_t mutator_high_idx = -1; idx_t mutator_empty_low_idx = _partitions.max(); @@ -2775,10 +2625,6 @@ size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_o ShenandoahFreeSetPartitionId::Collector, ac); collector_available += ac; regions_to_collector++; - if (ac != region_size_bytes) { - young_used_regions++; - young_used_bytes = region_size_bytes - ac; - } log_trace(gc, free)(" Shifting region %zu from mutator_free to collector_free", idx); log_trace(gc, free)(" Shifted Mutator range [%zd, %zd]," @@ -2805,10 +2651,6 @@ size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_o if ((ac == region_size_bytes) && (idx > mutator_empty_high_idx)) { mutator_empty_high_idx = idx; } - if (ac != region_size_bytes) { - young_used_regions++; - young_used_bytes += region_size_bytes - ac; - } } else { // Region is not in Mutator partition. Do the accounting. ShenandoahFreeSetPartitionId p = _partitions.membership(idx); @@ -2817,23 +2659,12 @@ size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_o assert(p != ShenandoahFreeSetPartitionId::Collector, "Collector regions must be converted from Mutator regions"); if (p == ShenandoahFreeSetPartitionId::OldCollector) { assert(!r->is_empty(), "Empty regions should be in Mutator partition at entry to reserve_regions"); - old_used_regions++; - old_used_bytes = region_size_bytes - ac; // This region is within the range for OldCollector partition, as established by find_regions_with_alloc_capacity() assert((_partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector) <= idx) && (_partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector) >= idx), "find_regions_with_alloc_capacity() should have established this is in range"); } else { assert(p == ShenandoahFreeSetPartitionId::NotFree, "sanity"); - // This region has been retired - if (r->is_old()) { - old_used_regions++; - old_used_bytes += region_size_bytes - ac; - } else { - assert(r->is_young(), "Retired region should be old or young"); - young_used_regions++; - young_used_bytes += region_size_bytes - ac; - } } } } @@ -3125,33 +2956,6 @@ void ShenandoahFreeSet::decrease_humongous_waste_for_regular_bypass(ShenandoahHe _total_humongous_waste -= waste; } - -HeapWord* ShenandoahFreeSet::allocate(ShenandoahAllocRequest& req, bool& in_new_region) { - shenandoah_assert_heaplocked(); - if (ShenandoahHeapRegion::requires_humongous(req.size())) { - switch (req.type()) { - case ShenandoahAllocRequest::_alloc_shared: - case ShenandoahAllocRequest::_alloc_shared_gc: - in_new_region = true; - return allocate_contiguous(req, /* is_humongous = */ true); - case ShenandoahAllocRequest::_alloc_cds: - in_new_region = true; - return allocate_contiguous(req, /* is_humongous = */ false); - case ShenandoahAllocRequest::_alloc_plab: - case ShenandoahAllocRequest::_alloc_gclab: - case ShenandoahAllocRequest::_alloc_tlab: - in_new_region = false; - assert(false, "Trying to allocate TLAB in humongous region: %zu", req.size()); - return nullptr; - default: - ShouldNotReachHere(); - return nullptr; - } - } else { - return allocate_single(req, in_new_region); - } -} - void ShenandoahFreeSet::print_on(outputStream* out) const { out->print_cr("Mutator Free Set: %zu", _partitions.count(ShenandoahFreeSetPartitionId::Mutator)); ShenandoahLeftRightIterator mutator(const_cast(&_partitions), ShenandoahFreeSetPartitionId::Mutator); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 7481b81c9c6..4ee9933794d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -161,7 +161,7 @@ public: _membership[int(p)].clear_bit(idx); } - inline void one_region_is_no_longer_empty(ShenandoahFreeSetPartitionId partition); + void one_region_is_no_longer_empty(ShenandoahFreeSetPartitionId partition); // Set the Mutator intervals, usage, and capacity according to arguments. Reset the Collector intervals, used, capacity // to represent empty Collector free set. We use this at the end of rebuild_free_set() to avoid the overhead of making @@ -231,11 +231,11 @@ public: const char* partition_membership_name(idx_t idx) const; // Return the index of the next available region >= start_index, or maximum_regions if not found. - inline idx_t find_index_of_next_available_region(ShenandoahFreeSetPartitionId which_partition, + idx_t find_index_of_next_available_region(ShenandoahFreeSetPartitionId which_partition, idx_t start_index) const; // Return the index of the previous available region <= last_index, or -1 if not found. - inline idx_t find_index_of_previous_available_region(ShenandoahFreeSetPartitionId which_partition, + idx_t find_index_of_previous_available_region(ShenandoahFreeSetPartitionId which_partition, idx_t last_index) const; // Return the index of the next available cluster of cluster_size regions >= start_index, or maximum_regions if not found. @@ -279,12 +279,12 @@ public: // leftmost() and leftmost_empty() return _max, rightmost() and rightmost_empty() return 0 // otherwise, expect the following: // 0 <= leftmost <= leftmost_empty <= rightmost_empty <= rightmost < _max - inline idx_t leftmost(ShenandoahFreeSetPartitionId which_partition) const; - inline idx_t rightmost(ShenandoahFreeSetPartitionId which_partition) const; + idx_t leftmost(ShenandoahFreeSetPartitionId which_partition) const; + idx_t rightmost(ShenandoahFreeSetPartitionId which_partition) const; idx_t leftmost_empty(ShenandoahFreeSetPartitionId which_partition); idx_t rightmost_empty(ShenandoahFreeSetPartitionId which_partition); - inline bool is_empty(ShenandoahFreeSetPartitionId which_partition) const; + bool is_empty(ShenandoahFreeSetPartitionId which_partition) const; inline void increase_region_counts(ShenandoahFreeSetPartitionId which_partition, size_t regions); inline void decrease_region_counts(ShenandoahFreeSetPartitionId which_partition, size_t regions); @@ -315,7 +315,7 @@ public: inline void decrease_available(ShenandoahFreeSetPartitionId which_partition, size_t bytes); inline size_t get_available(ShenandoahFreeSetPartitionId which_partition); - inline void increase_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); + void increase_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); inline void decrease_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); inline size_t get_used(ShenandoahFreeSetPartitionId which_partition) { assert (which_partition < NumPartitions, "Partition must be valid"); @@ -408,7 +408,7 @@ public: void assert_bounds() NOT_DEBUG_RETURN; // this checks certain sanity conditions related to the bounds with much less effort than is required to // more rigorously enforce correctness as is done by assert_bounds() - inline void assert_bounds_sanity() NOT_DEBUG_RETURN; + void assert_bounds_sanity() NOT_DEBUG_RETURN; }; // Publicly, ShenandoahFreeSet represents memory that is available to mutator threads. The public capacity(), used(), @@ -436,6 +436,7 @@ public: // during the next GC pass. class ShenandoahFreeSet : public CHeapObj { + using idx_t = ShenandoahSimpleBitMap::idx_t; private: ShenandoahHeap* const _heap; @@ -560,23 +561,6 @@ private: #endif } - // Increases used memory for the partition if the allocation is successful. `in_new_region` will be set - // if this is the first allocation in the region. - HeapWord* try_allocate_in(ShenandoahHeapRegion* region, ShenandoahAllocRequest& req, bool& in_new_region); - - // While holding the heap lock, allocate memory for a single object or LAB which is to be entirely contained - // within a single HeapRegion as characterized by req. - // - // Precondition: !ShenandoahHeapRegion::requires_humongous(req.size()) - HeapWord* allocate_single(ShenandoahAllocRequest& req, bool& in_new_region); - - // While holding the heap lock, allocate memory for a humongous object which spans one or more regions that - // were previously empty. Regions that represent humongous objects are entirely dedicated to the humongous - // object. No other objects are packed into these regions. - // - // Precondition: ShenandoahHeapRegion::requires_humongous(req.size()) - HeapWord* allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous); - bool transfer_one_region_from_mutator_to_old_collector(size_t idx, size_t alloc_capacity); // Change region r from the Mutator partition to the GC's Collector or OldCollector partition. This requires that the @@ -591,39 +575,16 @@ private: // Return true if and only if the given region is successfully flipped to the old partition bool flip_to_old_gc(ShenandoahHeapRegion* r); - // Handle allocation for mutator. - HeapWord* allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region); - // Update allocation bias and decided whether to allocate from the left or right side of the heap. void update_allocation_bias(); - // Search for regions to satisfy allocation request using iterator. - template - HeapWord* allocate_from_regions(Iter& iterator, ShenandoahAllocRequest &req, bool &in_new_region); - - // Handle allocation for collector (for evacuation). - HeapWord* allocate_for_collector(ShenandoahAllocRequest& req, bool& in_new_region); - - // Search for allocation in region with same affiliation as request, using given iterator, - // or affiliate the first usable FREE region with given affiliation and allocate in. - template - HeapWord* allocate_with_affiliation(Iter& iterator, - ShenandoahAffiliation affiliation, - ShenandoahAllocRequest& req, - bool& in_new_region); - - // Attempt to allocate memory for an evacuation from the mutator's partition. - HeapWord* try_allocate_from_mutator(ShenandoahAllocRequest& req, bool& in_new_region); - void clear_internal(); // Returns true iff this region is entirely available, either because it is empty() or because it has been found to represent // immediate trash and we'll be able to immediately recycle it. Note that we cannot recycle immediate trash if // concurrent weak root processing is in progress. - inline bool can_allocate_from(ShenandoahHeapRegion *r) const; - inline bool can_allocate_from(size_t idx) const; - - inline bool has_alloc_capacity(ShenandoahHeapRegion *r) const; + bool can_allocate_from(ShenandoahHeapRegion *r) const; + bool can_allocate_from(size_t idx) const; void transfer_empty_regions_from_to(ShenandoahFreeSetPartitionId source_partition, ShenandoahFreeSetPartitionId dest_partition, @@ -661,9 +622,36 @@ public: return _partitions.shrink_interval_if_range_modifies_either_boundary(partition, low_idx, high_idx, num_regions); } + // Called by ShenandoahAllocator after a successful allocation to update used/affiliated totals. + // boundary_changed indicates if partition boundaries were modified (retire or new-region), + // triggering a full bounds validation in debug builds. + void notify_allocation(ShenandoahFreeSetPartitionId partition, bool in_new_region, bool boundary_changed); + + // Find a region in the given partition with at least min_size_words of allocatable capacity. + // Handles bias direction, trash recycling, and affiliation setup for new (empty) regions. + // Returns nullptr if no suitable region found. Sets in_new_region if the returned region was empty. + // Caller must hold the heap lock. + template + ShenandoahHeapRegion* find_region_for_alloc(size_t min_size_words, bool& in_new_region); + + // Steal an empty region from the Mutator partition for the given collector partition. + // Flips the region, sets up affiliation, and returns it ready for allocation. + // The returned region is always empty (newly available for allocation). + // Returns nullptr if no region can be stolen. Caller must hold the heap lock. + ShenandoahHeapRegion* steal_from_mutator(ShenandoahFreeSetPartitionId target_partition, + ShenandoahAllocRequest& req); + + // Allocate contiguous regions for humongous objects. Caller must hold heap lock. + HeapWord* allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous); + + // Partition accounting APIs for allocators. + void increase_partition_used(ShenandoahFreeSetPartitionId partition, size_t bytes); + void mark_region_used(ShenandoahFreeSetPartitionId partition); + size_t retire_region(ShenandoahFreeSetPartitionId partition, size_t idx, size_t used_bytes); + // Public because ShenandoahRegionPartitions assertions require access. - inline size_t alloc_capacity(ShenandoahHeapRegion *r) const; - inline size_t alloc_capacity(size_t idx) const; + size_t alloc_capacity(ShenandoahHeapRegion *r) const; + size_t alloc_capacity(size_t idx) const; // Return bytes used by old inline size_t old_used() { @@ -833,8 +821,6 @@ public: void decrease_humongous_waste_for_regular_bypass(ShenandoahHeapRegion* r, size_t waste); - HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); - /* * Internal fragmentation metric: describes how fragmented the heap regions are. * @@ -894,8 +880,7 @@ public: // OldCollector partition. Upon return, old_region_count holds the updated number of regions in the OldCollector partition. // // Returns allocatable memory within Mutator partition, in words. - size_t reserve_regions(size_t to_reserve, size_t old_reserve, size_t &old_region_count, - size_t &young_used_regions, size_t &old_used_regions, size_t &young_used_bytes, size_t &old_used_bytes); + size_t reserve_regions(size_t to_reserve, size_t old_reserve, size_t &old_region_count); // Reserve space for evacuations, with regions reserved for old evacuations placed to the right // of regions reserved of young evacuations. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index cd04db383ed..cab0db7e78a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -136,6 +136,7 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) { void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { ShenandoahHeap* heap = ShenandoahHeap::heap(); + heap->release_injected_pins(); // A full GC may be entered directly, or as an upgrade from a failed // degenerated GC. In the latter case, self-forwarded objects may be diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index f522a33a31c..ac1feacd74d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -53,7 +53,8 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : _requested_generation(nullptr), _gc_mode(none), _degen_point(ShenandoahGC::_degenerated_unset), - _heap(ShenandoahGenerationalHeap::heap()) { + _heap(ShenandoahGenerationalHeap::heap()), + _do_old_gc_bootstrap(false) { shenandoah_assert_generational(); set_name("ShenControl"); create_and_start(); @@ -529,12 +530,14 @@ void ShenandoahGenerationalControlThread::service_concurrent_cycle(ShenandoahGen assert(!generation->is_old(), "Old GC takes a different control path"); + _do_old_gc_bootstrap = do_old_gc_bootstrap; ShenandoahConcurrentGC gc(generation, do_old_gc_bootstrap); _heap->increment_total_collections(false); if (gc.collect(cause)) { // Cycle is complete _heap->notify_gc_progress(); generation->record_success_concurrent(gc.abbreviated()); + _do_old_gc_bootstrap = false; } else { assert(_heap->cancelled_gc(), "Must have been cancelled"); check_cancellation_or_degen(gc.degen_point()); @@ -605,6 +608,7 @@ void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause ShenandoahFullGC gc; gc.collect(cause); _degen_point = ShenandoahGC::_degenerated_unset; + _do_old_gc_bootstrap = false; } void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const ShenandoahGCRequest& request) { @@ -613,9 +617,10 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh ShenandoahGCSession session(request.cause, request.generation, true, _degen_point == ShenandoahGC::ShenandoahDegenPoint::_degenerated_outside_cycle); - ShenandoahDegenGC gc(_degen_point, request.generation); + ShenandoahDegenGC gc(_degen_point, request.generation, _do_old_gc_bootstrap); gc.collect(request.cause); _degen_point = ShenandoahGC::_degenerated_unset; + _do_old_gc_bootstrap = false; assert(_heap->young_generation()->task_queues()->is_empty(), "Unexpected young generation marking tasks"); if (request.generation->is_global()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp index 5a3ab25eabe..358299ea469 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp @@ -84,6 +84,9 @@ private: // preparing for mark). ShenandoahSharedFlag _allow_old_preemption; + // True while the current cycle is the bootstrap of an old GC. + bool _do_old_gc_bootstrap; + public: ShenandoahGenerationalControlThread(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 750022b274e..54ab4c27038 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -62,10 +62,12 @@ ShenandoahGenerationalEvacuationTask::ShenandoahGenerationalEvacuationTask(Shena void ShenandoahGenerationalEvacuationTask::work(uint worker_id) { if (_concurrent) { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_evac, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahConcurrentWorkerSession worker_session(worker_id); SuspendibleThreadSetJoiner stsj; do_work(); } else { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::degen_gc_evac, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahParallelWorkerSession worker_session(worker_id); do_work(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp index 43dbddda9f7..f2411702e35 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp @@ -144,7 +144,7 @@ void ShenandoahGenerationalFullGC::account_for_region(ShenandoahHeapRegion* r, s void ShenandoahGenerationalFullGC::maybe_coalesce_and_fill_region(ShenandoahHeapRegion* r) { if (r->is_pinned() && r->is_old() && r->is_active() && !r->is_humongous()) { r->begin_preemptible_coalesce_and_fill(); - r->oop_coalesce_and_fill(false); + r->oop_coalesce_and_fill(/* cancellable = */ false, /* do_card_table_updates = */ false); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 7170c88cd43..31129182380 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -26,6 +26,7 @@ #include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" @@ -207,7 +208,7 @@ oop ShenandoahGenerationalHeap::evacuate_object(oop p, Thread* thread) { markWord mark = p->mark(); if (mark.is_marked()) { // Already forwarded. - return ShenandoahBarrierSet::resolve_forwarded(p); + return ShenandoahForwarding::get_forwardee(p); } if (mark.has_displaced_mark_helper()) { @@ -721,10 +722,12 @@ public: void work(uint worker_id) override { if (CONCURRENT) { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_update_refs, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahConcurrentWorkerSession worker_session(worker_id); SuspendibleThreadSetJoiner stsj; do_work(worker_id); } else { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::degen_gc_update_refs, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahParallelWorkerSession worker_session(worker_id); do_work(worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c0df4bbe10c..7731ad911c5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -42,6 +42,7 @@ #include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/mode/shenandoahSATBMode.hpp" +#include "gc/shenandoah/shenandoahAllocator.hpp" #include "gc/shenandoah/shenandoahAllocRate.inline.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" @@ -67,6 +68,7 @@ #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp" +#include "gc/shenandoah/shenandoahPartitionAllocator.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" @@ -434,6 +436,7 @@ jint ShenandoahHeap::initialize() { } _free_set = new ShenandoahFreeSet(this, _num_regions); + _allocator = new ShenandoahAllocator(_free_set); initialize_generations(); // We are initializing free set. We ignore cset region tallies. @@ -575,6 +578,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _shenandoah_policy(policy), _gc_mode(nullptr), _free_set(nullptr), + _allocator(nullptr), _verifier(nullptr), _phase_timings(nullptr), _monitoring_support(nullptr), @@ -591,7 +595,8 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _aux_bitmap_region_special(false), _liveness_cache(nullptr), _collection_set(nullptr), - _evac_tracker(new ShenandoahEvacuationTracker()) + _evac_tracker(new ShenandoahEvacuationTracker()), + _injected_pin_count(0) { // Initialize GC mode early, many subsequent initialization procedures depend on it initialize_mode(); @@ -941,7 +946,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { if (req.is_mutator_alloc()) { if (!ShenandoahAllocFailureALot || !should_inject_alloc_failure()) { - result = allocate_memory_under_lock(req, in_new_region); + result = allocate_memory_work(req, in_new_region); } // Check that gc overhead is not exceeded. @@ -973,7 +978,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { const size_t original_count = shenandoah_policy()->full_gc_count(); while (result == nullptr && should_retry_allocation(original_count)) { control_thread()->handle_alloc_failure(req, true); - result = allocate_memory_under_lock(req, in_new_region); + result = allocate_memory_work(req, in_new_region); } if (result != nullptr) { // If our allocation request has been satisfied after it initially failed, we count this as good gc progress @@ -989,7 +994,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { } } else { assert(req.is_gc_alloc(), "Can only accept GC allocs here"); - result = allocate_memory_under_lock(req, in_new_region); + result = allocate_memory_work(req, in_new_region); // Do not call handle_alloc_failure() here, because we cannot block. // The allocation failure would be handled by the LRB slowpath with handle_alloc_failure_evac(). } @@ -1019,21 +1024,15 @@ inline bool ShenandoahHeap::should_retry_allocation(size_t original_full_gc_coun && !shenandoah_policy()->is_at_shutdown(); } -HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req, bool& in_new_region) { - // If we are dealing with mutator allocation, then we may need to block for safepoint. - // We cannot block for safepoint for GC allocations, because there is a high chance - // we are already running at safepoint or from stack watermark machinery, and we cannot - // block again. - ShenandoahHeapLocker locker(lock(), req.is_mutator_alloc()); - - // Make sure the old generation has room for either evacuations or promotions before trying to allocate. - if (req.is_old() && !old_generation()->can_allocate(req)) { +HeapWord* ShenandoahHeap::allocate_memory_work(ShenandoahAllocRequest& req, bool& in_new_region) { + // Reserve the promotion budget up front so it is enforced atomically without the heap lock. + // If the reserve is exhausted, deny the promotion rather than overshoot it; the reservation + // is refunded below if the allocation itself fails. + if (req.is_promotion() && !old_generation()->try_expend_promoted(req.size() << LogHeapWordSize)) { return nullptr; } - // If TLAB request size is greater than available, allocate() will attempt to downsize request to fit within available - // memory. - HeapWord* result = _free_set->allocate(req, in_new_region); + HeapWord* result = _allocator->allocate(req, in_new_region); if (result != nullptr) { if (req.is_mutator_alloc()) { @@ -1044,13 +1043,13 @@ HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req if (req.is_lab_alloc()) { old_generation()->configure_plab_for_current_thread(req); } else if (req.is_promotion()) { - const size_t actual_size = req.actual_size() * HeapWordSize; - log_debug(gc, plab)("Expend shared promotion of %zu bytes", actual_size); - old_generation()->expend_promoted(actual_size); + log_debug(gc, plab)("Expend shared promotion of %zu bytes", req.actual_size() * HeapWordSize); } } + } else if (req.is_promotion()) { + // Allocation failed, so refund the promotion budget reserved above. + old_generation()->unexpend_promoted(req.size() << LogHeapWordSize); } - return result; } @@ -1238,7 +1237,6 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { // A cancellation at this point means the degenerated cycle must resume from update-refs. set_gc_state_concurrent(EVACUATION, false); - set_gc_state_concurrent(WEAK_ROOTS, false); set_gc_state_concurrent(UPDATE_REFS, true); } @@ -1254,35 +1252,24 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { _update_refs_iterator.reset(); } -class ShenandoahCompositeHandshakeClosure : public HandshakeClosure { - HandshakeClosure* _handshake_1; - HandshakeClosure* _handshake_2; - public: - ShenandoahCompositeHandshakeClosure(HandshakeClosure* handshake_1, HandshakeClosure* handshake_2) : - HandshakeClosure(handshake_2->name()), - _handshake_1(handshake_1), _handshake_2(handshake_2) {} - - void do_thread(Thread* thread) override { - _handshake_1->do_thread(thread); - _handshake_2->do_thread(thread); - } -}; - -void ShenandoahHeap::concurrent_final_roots(HandshakeClosure* handshake_closure) { +void ShenandoahHeap::concurrent_final_roots() { { - assert(!is_evacuation_in_progress(), "Should not evacuate for abbreviated or old cycles"); MutexLocker lock(Threads_lock); + +#ifdef ASSERT + for (JavaThreadIteratorWithHandle jtiwh; JavaThread* jt = jtiwh.next();) { + StackWatermark* sw = StackWatermarkSet::get(jt, StackWatermarkKind::gc); + assert(sw == nullptr || sw->processing_completed(), + "Cannot turn off weak roots before stack watermark processing is complete"); + } +#endif + set_gc_state_concurrent(WEAK_ROOTS, false); } ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value()); Threads::non_java_threads_do(&propagator); - if (handshake_closure == nullptr) { - Handshake::execute(&propagator); - } else { - ShenandoahCompositeHandshakeClosure composite(&propagator, handshake_closure); - Handshake::execute(&composite); - } + Handshake::execute(&propagator); } oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { @@ -2766,6 +2753,39 @@ bool ShenandoahHeap::should_inject_alloc_failure() { return _inject_alloc_failure.is_set() && _inject_alloc_failure.try_unset(); } +void ShenandoahHeap::try_inject_pin() { + assert(!ShenandoahSafepoint::is_at_shenandoah_safepoint(), "try_inject_pin() must be called outside a safepoint."); + assert(active_generation() != nullptr, "Active generation must be set before we inject pins."); + assert(is_concurrent_mark_in_progress() || active_generation()->is_mark_complete(), + "try_inject_pin() requires marking is in progress or has completed."); + if (ShenandoahPinRegionRate && !cancelled_gc() && ((uintx)(os::random() % 1000) < ShenandoahPinRegionRate) && + _injected_pin_count < MAX_INJECTED_PINS) { + const size_t idx = os::random() % num_regions(); + ShenandoahHeapRegion* r = get_region(idx); + if ((r->is_regular() || r->is_humongous_start()) && r->has_live()) { + r->record_pin(); + _injected_pin_indices[_injected_pin_count] = idx; + _injected_pin_count++; + } + } +} + +void ShenandoahHeap::release_injected_pins() { + if (_injected_pin_count == 0) { + return; + } + + assert(_injected_pin_count <= MAX_INJECTED_PINS, + "Injected pin count: %u exceeds max: %u.", _injected_pin_count, MAX_INJECTED_PINS); + for (uint i = 0; i < _injected_pin_count; i++) { + const size_t idx = _injected_pin_indices[i]; + ShenandoahHeapRegion* r = get_region(idx); + assert(r->pin_count() > 0, "Region %zu in tracker must contain a pin.", idx); + r->record_unpin(); + } + _injected_pin_count = 0; +} + void ShenandoahHeap::initialize_serviceability() { _memory_pool = new ShenandoahMemoryPool(this); _cycle_memory_manager.add_pool(_memory_pool); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 33d5fa6b04f..171f473d06a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -48,6 +48,7 @@ class ConcurrentGCTimer; class ObjectIterateScanRootClosure; +class ShenandoahAllocator; class ShenandoahCollectorPolicy; class ShenandoahGCSession; class ShenandoahGCStateResetter; @@ -492,8 +493,8 @@ private: // Retires LABs used for evacuation void concurrent_prepare_for_update_refs(); - // Turn off weak roots flag, purge old satb buffers in generational mode - void concurrent_final_roots(HandshakeClosure* handshake_closure = nullptr); + // Turn off weak roots flag + void concurrent_final_roots(); virtual void update_heap_references(ShenandoahGeneration* generation, bool concurrent); // Final update region states @@ -533,6 +534,7 @@ private: ShenandoahCollectorPolicy* _shenandoah_policy; ShenandoahMode* _gc_mode; ShenandoahFreeSet* _free_set; + ShenandoahAllocator* _allocator; ShenandoahVerifier* _verifier; ShenandoahPhaseTimings* _phase_timings; @@ -557,6 +559,7 @@ public: ShenandoahCollectorPolicy* shenandoah_policy() const { return _shenandoah_policy; } ShenandoahMode* mode() const { return _gc_mode; } ShenandoahFreeSet* free_set() const { return _free_set; } + ShenandoahAllocator* allocator() const { return _allocator; } ShenandoahPhaseTimings* phase_timings() const { return _phase_timings; } @@ -699,7 +702,7 @@ protected: inline HeapWord* allocate_from_gclab(Thread* thread, size_t size); private: - HeapWord* allocate_memory_under_lock(ShenandoahAllocRequest& request, bool& in_new_region); + HeapWord* allocate_memory_work(ShenandoahAllocRequest& request, bool& in_new_region); HeapWord* allocate_from_gclab_slow(Thread* thread, size_t size); HeapWord* allocate_new_gclab(size_t min_size, size_t word_size, size_t* actual_size); @@ -865,6 +868,19 @@ private: void try_inject_alloc_failure(); bool should_inject_alloc_failure(); + + // Randomly pin a region when ShenandoahPinRegionRate > 0. Pin injection is only called after + // the cycle has populated _live_data and runs concurrently on the control thread. Releasing + // injected pins is done at the start of every cycle preventing stale pinned region states. + void try_inject_pin(); + void release_injected_pins(); + + // Maximum number of regions that can be injected with pins. + static const uint MAX_INJECTED_PINS = 32; + + // Tracker for injected pins added by try_inject_pin(). + size_t _injected_pin_indices[MAX_INJECTED_PINS]; + uint _injected_pin_count; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index b8db14e5ee7..b3c847cadaf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -45,6 +45,7 @@ #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahPrefetch.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "oops/compressedOops.inline.hpp" @@ -110,7 +111,7 @@ inline void ShenandoahHeap::non_conc_update_with_forwarded(T* p) { // set that are not really forwarded. We can still go and try and update them // (uselessly) to simplify the common path. shenandoah_assert_forwarded_except(p, obj, cancelled_gc()); - oop fwd = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); shenandoah_assert_not_in_cset_except(p, fwd, cancelled_gc()); // Unconditionally store the update: no concurrent updates expected. @@ -129,7 +130,7 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) { // set that are not really forwarded. We can still go and try CAS-update them // (uselessly) to simplify the common path. shenandoah_assert_forwarded_except(p, obj, cancelled_gc()); - oop fwd = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); shenandoah_assert_not_in_cset_except(p, fwd, cancelled_gc()); // Sanity check: we should not be updating the cset regions themselves, @@ -515,74 +516,35 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, template inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, T* cl, HeapWord* limit) { - assert(! region->is_humongous_continuation(), "no humongous continuation regions here"); + assert(!region->is_humongous_continuation(), "no humongous continuation regions here"); + assert(limit <= region->top(), "sanity"); ShenandoahMarkingContext* const ctx = marking_context(); HeapWord* tams = ctx->top_at_mark_start(region); - - size_t skip_bitmap_delta = 1; - HeapWord* start = region->bottom(); - HeapWord* end = MIN2(tams, region->end()); - - // Step 1. Scan below the TAMS based on bitmap data. HeapWord* limit_bitmap = MIN2(limit, tams); + // Step 1. Scan below the TAMS based on bitmap data. // Try to scan the initial candidate. If the candidate is above the TAMS, it would // fail the subsequent "< limit_bitmap" checks, and fall through to Step 2. - HeapWord* cb = ctx->get_next_marked_addr(start, end); + HeapWord* cb = ctx->get_next_marked_addr(region->bottom(), limit_bitmap); + while (cb < limit_bitmap) { + assert (cb < tams, "only objects below TAMS here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(tams)); + assert (cb < limit, "only objects below limit here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(limit)); + oop obj = cast_to_oop(cb); + assert(oopDesc::is_oop(obj), "sanity"); + assert(ctx->is_marked(obj), "object expected to be marked"); - intx dist = ShenandoahMarkScanPrefetch; - if (dist > 0) { - // Batched scan that prefetches the oop data, anticipating the access to - // either header, oop field, or forwarding pointer. Not that we cannot - // touch anything in oop, while it still being prefetched to get enough - // time for prefetch to work. This is why we try to scan the bitmap linearly, - // disregarding the object size. However, since we know forwarding pointer - // precedes the object, we can skip over it. Once we cannot trust the bitmap, - // there is no point for prefetching the oop contents, as oop->size() will - // touch it prematurely. - - // No variable-length arrays in standard C++, have enough slots to fit - // the prefetch distance. - static const int SLOT_COUNT = 256; - guarantee(dist <= SLOT_COUNT, "adjust slot count"); - HeapWord* slots[SLOT_COUNT]; - - int avail; - do { - avail = 0; - for (int c = 0; (c < dist) && (cb < limit_bitmap); c++) { - Prefetch::read(cb, oopDesc::mark_offset_in_bytes()); - slots[avail++] = cb; - cb += skip_bitmap_delta; - if (cb < limit_bitmap) { - cb = ctx->get_next_marked_addr(cb, limit_bitmap); - } - } - - for (int c = 0; c < avail; c++) { - assert (slots[c] < tams, "only objects below TAMS here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(slots[c]), p2i(tams)); - assert (slots[c] < limit, "only objects below limit here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(slots[c]), p2i(limit)); - oop obj = cast_to_oop(slots[c]); - assert(oopDesc::is_oop(obj), "sanity"); - assert(ctx->is_marked(obj), "object expected to be marked"); - cl->do_object(obj); - } - } while (avail > 0); - } else { - while (cb < limit_bitmap) { - assert (cb < tams, "only objects below TAMS here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(tams)); - assert (cb < limit, "only objects below limit here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(limit)); - oop obj = cast_to_oop(cb); - assert(oopDesc::is_oop(obj), "sanity"); - assert(ctx->is_marked(obj), "object expected to be marked"); - cl->do_object(obj); - cb += skip_bitmap_delta; - if (cb < limit_bitmap) { - cb = ctx->get_next_marked_addr(cb, limit_bitmap); - } + // Compute the next object address and initiate prefetches for it, + // while we are processing current object. + constexpr size_t skip_bitmap_delta = 1; + cb += skip_bitmap_delta; + if (cb < limit_bitmap) { + cb = ctx->get_next_marked_addr(cb, limit_bitmap); } + ShenandoahPrefetch::prefetch(cast_to_oop(cb)); + + cl->do_object(obj); } // Step 2. Accurate size-based traversal, happens past the TAMS. @@ -595,9 +557,13 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, oop obj = cast_to_oop(cs); assert(oopDesc::is_oop(obj), "sanity"); assert(ctx->is_marked(obj), "object expected to be marked"); - size_t size = ShenandoahForwarding::size(obj); + + // Compute the next object address and initiate prefetches for it, + // while we are processing current object. + cs += ShenandoahForwarding::size(obj); + ShenandoahPrefetch::prefetch(cast_to_oop(cs)); + cl->do_object(obj); - cs += size; } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index c5b9c929eed..66eaed2c222 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -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. * Copyright (c) 2013, 2020, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -32,7 +32,6 @@ #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" @@ -40,14 +39,11 @@ #include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" #include "memory/iterator.inline.hpp" -#include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" -#include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" -#include "runtime/safepoint.hpp" #include "utilities/powerOfTwo.hpp" size_t ShenandoahHeapRegion::RegionCount = 0; @@ -452,7 +448,7 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const { } // oop_iterate without closure, return true if completed without cancellation -bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable) { +bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable, bool do_card_table_updates) { assert(!is_humongous(), "No need to fill or coalesce humongous regions"); if (!is_active()) { @@ -489,7 +485,16 @@ bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable) { size_t fill_size = next_marked_obj - obj_addr; assert(fill_size >= ShenandoahHeap::min_fill_size(), "previously allocated object known to be larger than min_size"); ShenandoahHeap::fill_with_object(obj_addr, fill_size); - heap->old_generation()->card_scan()->coalesce_objects(obj_addr, fill_size); + if (do_card_table_updates) { + heap->old_generation()->card_scan()->coalesce_objects(obj_addr, fill_size); + } else { + // A humongous object allocation failure during evacuation will skip the degenerated cycle and + // jump straight to a full GC. If this region is pinned when the full GC cycle starts, it will + // not be compacted. Therefore, if the region is old, we must fill in any unmarked objects. However, + // promoted objects will not have been registered yet, so we cannot use the card table here. + assert(heap->is_full_gc_in_progress(), "Can only skip card table updates during a full GC"); + } + obj_addr = next_marked_obj; } if (cancellable && heap->cancelled_gc()) { @@ -837,16 +842,7 @@ void ShenandoahHeapRegion::set_affiliation(ShenandoahAffiliation new_affiliation p2i(top()), p2i(ctx->top_at_mark_start(this)), p2i(_update_watermark.load_relaxed()), p2i(ctx->top_bitmap(this))); } -#ifdef ASSERT - { - size_t idx = this->index(); - HeapWord* top_bitmap = ctx->top_bitmap(this); - - assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, _end), - "Region %zu, bitmap should be clear between top_bitmap: " PTR_FORMAT " and end: " PTR_FORMAT, idx, - p2i(top_bitmap), p2i(_end)); - } -#endif + shenandoah_assert_clear_above_top(this); if (region_affiliation == new_affiliation) { return; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index 9040a81848e..cf60f18e48f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -280,6 +280,7 @@ private: // clears the self_fwd bits. Safety-net reset on region recycle. ShenandoahSharedFlag _has_self_forwards; + // This is only read/written by a gc worker to avoid unnecessary bitmap resets bool _needs_bitmap_reset; public: @@ -447,7 +448,7 @@ public: // This is used by old-gen GC following concurrent marking to make old-gen HeapRegions parsable. Old regions must be // parsable because the mark bitmap is not reliable during the concurrent old mark. // Return true iff region is completely coalesced and filled. Returns false if cancelled before task is complete. - bool oop_coalesce_and_fill(bool cancellable); + bool oop_coalesce_and_fill(bool cancellable, bool do_card_table_updates = true); // Invoke closure on every reference contained within the humongous object that spans this humongous // region if the reference is contained within a DIRTY card and the reference is no more than words following diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp index 153193fa3a3..ffa087ac3c0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp @@ -153,7 +153,7 @@ void ShenandoahInPlacePromoter::maybe_promote_region(ShenandoahHeapRegion* r) co if (!obj->is_typeArray()) { promote_humongous(r); } - } else if (r->is_regular() && (r->get_top_before_promote() != nullptr)) { + } else if (r->is_regular_or_regular_pinned() && (r->get_top_before_promote() != nullptr)) { // Likewise, we cannot put promote-in-place regions into the collection set because that would also trigger // the LRB to copy on reference fetch. // @@ -181,7 +181,7 @@ void ShenandoahInPlacePromoter::promote(ShenandoahHeapRegion* region) const { assert(region->garbage_before_padded_for_promote() < old_garbage_threshold, "Region %zu has too much garbage for promotion", region->index()); assert(region->is_young(), "Only young regions can be promoted"); - assert(region->is_regular(), "Use different service to promote humongous regions"); + assert(region->is_regular_or_regular_pinned(), "Use different service to promote humongous regions"); assert(_heap->is_tenurable(region), "Only promote regions that are sufficiently aged"); assert(region->get_top_before_promote() == tams, "Region %zu has been used for allocations before promotion", region->index()); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index a72c557a5fe..9354ef25f3d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -57,22 +57,35 @@ ShenandoahMark::ShenandoahMark(ShenandoahGeneration* generation) : template void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, StringDedup::Requests* const req, bool update_refs) { + ShenandoahObjToScanQueueSet* queues = task_queues(); ShenandoahObjToScanQueue* q = get_queue(w); ShenandoahObjToScanQueue* old_q = get_old_queue(w); ShenandoahReferenceProcessor *rp = _generation->ref_processor(); ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahLiveData* ld = heap->get_liveness_cache(w); + // Take outstanding work from queues not covered by current workers. + // We expect there is little work in those queues. + mark_drain_extra_queues(queues, q); + // TODO: We can clean up this if we figure out how to do templated oop closures that // play nice with specialized_oop_iterators. if (update_refs) { using Closure = ShenandoahMarkUpdateRefsClosure; Closure cl(q, rp, old_q); - mark_loop_work(&cl, ld, w, t, req); + if (UseCompressedOops) { + mark_loop_work(&cl, ld, w, t, req); + } else { + mark_loop_work(&cl, ld, w, t, req); + } } else { using Closure = ShenandoahMarkRefsClosure; Closure cl(q, rp, old_q); - mark_loop_work(&cl, ld, w, t, req); + if (UseCompressedOops) { + mark_loop_work(&cl, ld, w, t, req); + } else { + mark_loop_work(&cl, ld, w, t, req); + } } heap->flush_liveness_cache(w); @@ -120,53 +133,51 @@ void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, Shena } } -template +template +void ShenandoahMark::mark_drain_extra_queues(ShenandoahObjToScanQueueSet* queues, ShenandoahObjToScanQueue* local_q) { + uintx stride = ShenandoahMarkLoopStride; + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahMarkTask t; + + assert(queues->get_reserved() == heap->workers()->active_workers(), + "Safety: claimable queues do not intersect with worker queues: %u == %u", + queues->get_reserved(), heap->workers()->active_workers()); + + ShenandoahObjToScanQueue* q = queues->claim_next(); + while (q != nullptr) { + while (!q->is_empty()) { + if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { + return; + } + for (uint i = 0; i < stride; i++) { + if (q->pop(t)) { + local_q->push(t); + } else { + break; + } + } + } + q = queues->claim_next(); + } +} + +template void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req) { uintx stride = ShenandoahMarkLoopStride; ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahObjToScanQueueSet* queues = task_queues(); - ShenandoahObjToScanQueue* q; + ShenandoahObjToScanQueue* q = get_queue(worker_id); + ShenandoahObjToScanQueue* old_q = get_old_queue(worker_id); ShenandoahMarkTask t; assert(_generation->type() == GENERATION, "Sanity: %d != %d", _generation->type(), GENERATION); _generation->ref_processor()->set_mark_closure(worker_id, cl); - /* - * Process outstanding queues, if any. - * - * There can be more queues than workers. To deal with the imbalance, we claim - * extra queues first. Since marking can push new tasks into the queue associated - * with this worker id, we come back to process this queue in the normal loop. - */ - assert(queues->get_reserved() == heap->workers()->active_workers(), - "Need to reserve proper number of queues: reserved: %u, active: %u", queues->get_reserved(), heap->workers()->active_workers()); - - q = queues->claim_next(); - while (q != nullptr) { - if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { - return; - } - - for (uint i = 0; i < stride; i++) { - if (q->pop(t)) { - do_task(q, cl, live_data, req, &t, worker_id); - } else { - assert(q->is_empty(), "Must be empty"); - q = queues->claim_next(); - break; - } - } - } - q = get_queue(worker_id); - ShenandoahObjToScanQueue* old_q = get_old_queue(worker_id); - ShenandoahSATBBufferClosure drain_satb(q, old_q); SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); - /* - * Normal marking loop: - */ while (true) { if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { return; @@ -179,7 +190,7 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w for (uint i = 0; i < stride; i++) { if (q->pop(t) || queues->steal(worker_id, t)) { - do_task(q, cl, live_data, req, &t, worker_id); + do_task(q, cl, live_data, req, &t, worker_id); work++; } else { break; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index ee29c76dcaf..a2c363b2129 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -51,7 +51,8 @@ protected: public: template - static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); + ALWAYSINLINE + static void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); // Loom support void start_mark(); @@ -71,35 +72,47 @@ public: private: // ---------- Marking loop and tasks - template - inline void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); + template + ALWAYSINLINE + static void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); - template - inline void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); + template + ALWAYSINLINE + static void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); - template - inline void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); + template + ALWAYSINLINE + static void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, int chunk, int pow, bool weak); template - inline void count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id); + ALWAYSINLINE + static void count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id); - template - void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); + template + ALWAYSINLINE + static bool in_generation(ShenandoahHeap* const heap, oop obj); + + template + ALWAYSINLINE + static void mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); + + ALWAYSINLINE + static void mark_ref(ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak, oop obj); + + ALWAYSINLINE + static void dedup_string(oop obj, StringDedup::Requests* const req); template void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); - template - static bool in_generation(ShenandoahHeap* const heap, oop obj); + template + NOINLINE // Main hot loop, start inlining from here + void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); - template - static void mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); + template + NOINLINE // Utility loop, maybe hot, start inlining from here + void mark_drain_extra_queues(ShenandoahObjToScanQueueSet* queues, ShenandoahObjToScanQueue* local_q); - static void mark_ref(ShenandoahObjToScanQueue* q, - ShenandoahMarkingContext* const mark_context, - bool weak, oop obj); - - static inline void dedup_string(oop obj, StringDedup::Requests* const req); protected: template void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 71ef99b17ac..45cec71935b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -37,17 +37,17 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPrefetch.inline.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/iterator.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/prefetch.inline.hpp" #include "utilities/devirtualizer.inline.hpp" #include "utilities/powerOfTwo.hpp" -template +template void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id) { oop obj = task->obj(); @@ -55,32 +55,58 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD shenandoah_assert_marked(nullptr, obj); shenandoah_assert_not_in_cset_except(nullptr, obj, ShenandoahHeap::heap()->cancelled_gc()); + Klass* klass = obj->klass(); + // Are we in weak subgraph scan? bool weak = task->is_weak(); cl->set_weak(weak); if (task->is_not_chunked()) { - Klass* klass = obj->klass(); - if (klass->is_instance_klass()) { - // Case 1: Normal oop, process as usual. - if (STRING_DEDUP && (klass == vmClasses::String_klass())) { - dedup_string(obj, req); + // Dispatch based on object type. The case order does not seem to affect performance, + // so it matches the enum order for consistency. + switch (klass->kind()) { + case Klass::InstanceKlassKind: { + // Regular instance. + if (STRING_DEDUP && (klass == vmClasses::String_klass())) { + dedup_string(obj, req); + } + InstanceKlass::cast(klass)->oop_oop_iterate(obj, cl); + break; } - if (klass->is_stack_chunk_instance_klass()) { - // Loom doesn't support mixing of weak marking and strong marking of stack chunks. + case Klass::InstanceRefKlassKind: { + // (Weak) reference instance. + InstanceRefKlass::cast(klass)->oop_oop_iterate(obj, cl); + break; + } + case Klass::InstanceMirrorKlassKind: + case Klass::InstanceClassLoaderKlassKind: { + // Remaining rare classes, dispatch generically. + obj->oop_iterate(cl); + break; + } + case Klass::InstanceStackChunkKlassKind: { + // Stack chunk. Loom doesn't support mixing of weak marking and strong marking + // of stack chunks, upgrade to strong right away. cl->set_weak(false); + InstanceStackChunkKlass::cast(klass)->oop_oop_iterate(obj, cl); + break; + } + case Klass::TypeArrayKlassKind: { + // Primitive array. Do nothing, no oops there. We use the same + // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: + // We skip iterating over the klass pointer since we know that + // Universe::TypeArrayKlass never moves. + break; + } + case Klass::ObjArrayKlassKind: { + // Object array and no chunk is set. Must be the first + // time we visit it, start the chunked processing. + do_chunked_array_start(q, cl, obj, klass, weak); + break; + } + default: { + fatal("Unknown klass kind: %d", klass->kind()); } - obj->oop_iterate(cl); - } else if (klass->is_objArray_klass()) { - // Case 2: Object array instance and no chunk is set. Must be the first - // time we visit it, start the chunked processing. - do_chunked_array_start(q, cl, obj, klass, weak); - } else { - // Case 3: Primitive array. Do nothing, no oops there. We use the same - // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: - // We skip iterating over the klass pointer since we know that - // Universe::TypeArrayKlass never moves. - assert(klass->is_typeArray_klass(), "should be type array"); } // Count liveness the last: push the outstanding work to the queues first // Avoid double-counting objects that are visited twice due to upgrade @@ -89,12 +115,12 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD count_liveness(live_data, obj, klass, worker_id); } } else { - // Case 4: Array chunk, has sensible chunk id. Process it. - do_chunked_array(q, cl, obj, task->chunk(), task->pow(), weak); + // Object array chunk. Process it. + do_chunked_array(q, cl, obj, klass, task->chunk(), task->pow(), weak); } } -void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { +inline void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { assert(req != nullptr, "Should be available if dedup is enabled"); // Skip if already requested or dedup is forbidden. @@ -111,7 +137,7 @@ void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { } template -inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id) { +void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id) { const ShenandoahHeap* const heap = ShenandoahHeap::heap(); const size_t region_idx = heap->heap_region_index_containing(obj); ShenandoahHeapRegion* const region = heap->get_region(region_idx); @@ -154,8 +180,8 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob } } -template -inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { +template +void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); int len = array->length(); @@ -167,7 +193,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, if (len <= (int) ObjArrayMarkingStride*2) { // A few slices only, process directly - array->oop_iterate_elements_range(cl, 0, len); + ObjArrayKlass::cast(klass)->oop_oop_iterate_elements_range(array, cl, 0, len); } else { int bits = log2i_graceful(len); // Compensate for non-power-of-two arrays, cover the array in excess: @@ -216,13 +242,13 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, // Process the irregular tail, if present int from = last_idx; if (from < len) { - array->oop_iterate_elements_range(cl, from, len); + ObjArrayKlass::cast(klass)->oop_oop_iterate_elements_range(array, cl, from, len); } } } -template -inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { +template +void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, int chunk, int pow, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); @@ -246,7 +272,7 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, assert (0 < to && to <= len, "to is sane: %d/%d", to, len); #endif - array->oop_iterate_elements_range(cl, from, to); + ObjArrayKlass::cast(klass)->oop_oop_iterate_elements_range(array, cl, from, to); } template @@ -291,7 +317,7 @@ bool ShenandoahMark::in_generation(ShenandoahHeap* const heap, oop obj) { } template -inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { // Note: This is a very hot code path, so the code should be conditional on GENERATION template // parameter where possible, in order to generate the most efficient code. @@ -327,17 +353,19 @@ inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, } template<> -inline void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +ALWAYSINLINE +void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { mark_non_generational_ref(p, q, mark_context, weak); } template<> -inline void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +ALWAYSINLINE +void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { mark_non_generational_ref(p, q, mark_context, weak); } template -inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, +void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { oop o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { @@ -353,8 +381,8 @@ inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQ } inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, - ShenandoahMarkingContext* const mark_context, - bool weak, oop obj) { + ShenandoahMarkingContext* const mark_context, + bool weak, oop obj) { bool skip_live = false; bool marked; if (weak) { @@ -363,6 +391,7 @@ inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); } if (marked) { + ShenandoahPrefetch::prefetch(obj); bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); assert(pushed, "overflow queue should always succeed pushing"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp index 73bf3ecbeea..177d953d4f3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -160,13 +160,15 @@ public: // strong. // Words that have been marked final before or by a concurrent thread will be // upgraded to strong. In this case, this method also returns true. - inline bool mark_strong(HeapWord* w, bool& was_upgraded); + ALWAYSINLINE + bool mark_strong(HeapWord* w, bool& was_upgraded); // Mark word as 'weak' if it hasn't been marked weak or strong yet. // Return true if the word has been marked weak, false if it has already been // marked strong or weak or if another thread has beat us by marking it // strong or weak. - inline bool mark_weak(HeapWord* heap_addr); + ALWAYSINLINE + bool mark_weak(HeapWord* heap_addr); inline bool is_marked(HeapWord* addr) const; inline bool is_marked_strong(HeapWord* w) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp index b960beeb16c..87629cefb0d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * 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 @@ -27,6 +27,7 @@ #include "gc/shared/markBitMap.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.hpp" +#include "runtime/orderAccess.hpp" ShenandoahMarkingContext::ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions) : _mark_bit_map(heap_region, bitmap_region), @@ -90,6 +91,9 @@ void ShenandoahMarkingContext::clear_bitmap(ShenandoahHeapRegion* r) { if (top_bitmap > bottom) { _mark_bit_map.clear_range_large(MemRegion(bottom, top_bitmap)); + // All bitmap writes must complete before we update top at bitmap. If these writes were reordered, + // other threads could see stale marks above top, which is not valid. + OrderAccess::storestore(); _top_bitmaps[r->index()] = bottom; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp index d8e0c74ea4e..870a81e4588 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp @@ -55,8 +55,11 @@ public: * been marked by this thread. Returns false if the object has already been marked, * or if a competing thread succeeded in marking this object. */ - inline bool mark_strong(oop obj, bool& was_upgraded); - inline bool mark_weak(oop obj); + ALWAYSINLINE + bool mark_strong(oop obj, bool& was_upgraded); + + ALWAYSINLINE + bool mark_weak(oop obj); // Simple versions of marking accessors, to be used outside of marking (e.g. no possible concurrent updates) inline bool is_marked(oop obj) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 5b24cfc979a..b0573c3f677 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -124,7 +124,7 @@ void ShenandoahNMethod::heal_nmethod(nmethod* nm) { assert(data->lock()->owned_by_self(), "Must hold the lock"); ShenandoahHeap* const heap = ShenandoahHeap::heap(); - if (heap->is_concurrent_weak_root_in_progress() || + if ((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || heap->is_concurrent_strong_root_in_progress()) { heal_nmethod_metadata(data); } else if (heap->is_concurrent_mark_in_progress()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index ff441a0c868..c98b96c689b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -84,6 +84,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { auto heap = ShenandoahGenerationalHeap::heap(); assert(!_old_generation->is_doing_mixed_evacuations(), "Should not start an old gc with pending mixed evacuations"); assert(!_old_generation->is_preparing_for_mark(), "Old regions need to be parsable during concurrent mark."); + heap->release_injected_pins(); // Enable preemption of old generation mark. _allow_preemption.set(); @@ -134,7 +135,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { // return from here with weak roots in progress. This is not a valid gc state // for any young collections (or allocation failures) that interrupt the old // collection. - heap->concurrent_final_roots(); + entry_final_roots(); // After concurrent old marking finishes, we reclaim immediate garbage. Further, we may also want to expand OLD in order // to make room for anticipated promotions and/or for mixed evacuations. Mixed evacuations are especially likely to diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 9c174b18d26..c92f74364fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -132,16 +132,18 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) void ShenandoahOldGeneration::set_promoted_reserve(size_t new_val) { shenandoah_assert_heaplocked_or_safepoint(); - _promoted_reserve = new_val; + _promoted_reserve.store_relaxed(new_val); } size_t ShenandoahOldGeneration::get_promoted_reserve() const { - return _promoted_reserve; + return _promoted_reserve.load_relaxed(); } void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { shenandoah_assert_heaplocked_or_safepoint(); - _promoted_reserve += increment; + // Writers are serialized by the heap lock, so relaxed ordering is sufficient; the atomic RMW + // only guards against tearing the concurrent lock-free reader (get_promoted_reserve). + _promoted_reserve.fetch_then_add(increment, memory_order_relaxed); } void ShenandoahOldGeneration::reset_promoted_expended() { @@ -193,10 +195,19 @@ void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent) } } -size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { - shenandoah_assert_heaplocked_or_safepoint(); - assert(get_promoted_expended() + increment <= get_promoted_reserve(), "Do not expend more promotion than budgeted"); - return _promoted_expended.add_then_fetch(increment); +bool ShenandoahOldGeneration::try_expend_promoted(size_t increment) { + // The promote reserve rarely changes during evacuation(only when there is PIP region), so snapshot it once; + // only _promoted_expended is contended and re-read on CAS failure. + const size_t reserve = get_promoted_reserve(); + size_t cur = _promoted_expended.load_relaxed(); + while (cur + increment <= reserve) { + size_t prev = _promoted_expended.compare_exchange(cur, cur + increment); + if (prev == cur) { + return true; + } + cur = prev; + } + return false; } size_t ShenandoahOldGeneration::unexpend_promoted(size_t decrement) { @@ -245,12 +256,11 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc // The actual size of the allocation may be larger than the requested bytes (due to alignment on card boundaries). // If this puts us over our promotion budget, we need to disable future PLAB promotions for this thread. - if (can_promote(actual_size)) { + if (try_expend_promoted(actual_size)) { // Assume the entirety of this PLAB will be used for promotion. This prevents promotion from overreach. // When we retire this plab, we'll unexpend what we don't really use. log_debug(gc, plab)("Thread can promote using PLAB of %zu bytes. Expended: %zu, available: %zu", actual_size, get_promoted_expended(), get_promoted_reserve()); - expend_promoted(actual_size); shenandoah_plab->enable_promotions(); shenandoah_plab->set_actual_size(actual_size); } else { @@ -596,7 +606,7 @@ bool ShenandoahOldGeneration::validate_idle() { 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(!heap->young_generation()->is_old_marking_active(), "Cannot have old mark queues if IDLE"); assert(!_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot have coalesce and fill candidates in IDLE"); assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot have mixed collection candidates in IDLE"); return true; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 7e26d800e1d..61a3114f906 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -29,6 +29,7 @@ #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahScanRemembered.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" @@ -57,14 +58,16 @@ private: // and in addition to the evacuation reserve for intra-generation evacuations (ShenandoahGeneration::_evacuation_reserve). // If there is more data ready to be promoted than can fit within this reserve, the promotion of some objects will be // deferred until a subsequent evacuation pass. - size_t _promoted_reserve; + Atomic _promoted_reserve; // Bytes of old-gen memory expended on promotions. This may be modified concurrently // by mutators and gc workers when promotion LABs are retired during evacuation. It // is therefore always accessed through atomic operations. This is increased when a // PLAB is allocated for promotions. The value is decreased by the amount of memory // remaining in a PLAB when it is retired. + shenandoah_padding(0); Atomic _promoted_expended; + shenandoah_padding(1); // Represents the quantity of live bytes we expect to promote during the next GC cycle, either by // evacuation or by promote-in-place. This value is used by the young heuristic to trigger mixed collections. @@ -111,8 +114,10 @@ public: // This zeros out the expended promotion count after the promotion reserve is computed void reset_promoted_expended(); - // This is incremented when allocations are made to copy promotions into the old generation - size_t expend_promoted(size_t increment); + // Atomically reserve `increment` bytes of promotion budget. Returns true if the full amount + // was reserved without exceeding the reserve. Lock-free: safe to call without the heap lock. + // Use this to gate a promotion decision before promoting. + bool try_expend_promoted(size_t increment); // This is used to return unused memory from a retired promotion LAB size_t unexpend_promoted(size_t decrement); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp index f139f94fc8b..59ed97dd513 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp @@ -28,8 +28,6 @@ #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPLAB.hpp" #include "logging/log.hpp" -#include "runtime/globals.hpp" -#include "runtime/javaThread.hpp" #include "utilities/copy.hpp" ShenandoahPLAB::ShenandoahPLAB() : @@ -46,9 +44,7 @@ ShenandoahPLAB::ShenandoahPLAB() : } ShenandoahPLAB::~ShenandoahPLAB() { - if (_plab != nullptr) { - delete _plab; - } + delete _plab; } void ShenandoahPLAB::subtract_from_promoted(size_t increment) { @@ -119,7 +115,7 @@ HeapWord* ShenandoahPLAB::allocate_slow(size_t size, bool is_promotion) { } if (_plab->words_remaining() < plab_min_size) { - // Retire current PLAB. This takes care of any PLAB book-keeping. + // Retire current PLAB. This takes care of any PLAB bookkeeping. retire(); size_t actual_size = 0; @@ -191,10 +187,12 @@ void ShenandoahPLAB::retire() { log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize); _heap->old_generation()->unexpend_promoted(not_promoted); } - const size_t original_waste = _plab->waste(); - HeapWord* const top = _plab->top(); // 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(). + // We do _not_ need to register this remnant object with the card table because all paths where a PLAB object would + // be created are covered by a subsequent phase in the cycle. For the concurrent and degenerated cycles, all PLABs + // are retired in preparation for update-references. All objects in these PLABs will be registered by update-card-tables. + // For a full GC, the entire remembered set will be rebuilt in the final phase. Note also that an empty TLAB will _not_ + // create a filler object when it is retired. _plab->retire(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp new file mode 100644 index 00000000000..73c35bf65c5 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp @@ -0,0 +1,172 @@ +/* + * 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 "gc/shared/plab.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPartitionAllocator.hpp" +#include "logging/log.hpp" + +template +ShenandoahPartitionAllocator::ShenandoahPartitionAllocator(ShenandoahFreeSet* free_set) + : _free_set(free_set), + _alloc_region(nullptr) {} + +template +HeapWord* ShenandoahPartitionAllocator::allocate(ShenandoahAllocRequest& req, bool& in_new_region) { + // Mutator allocations may yield to safepoint; GC allocations cannot. + ShenandoahHeapLocker locker(ShenandoahHeap::heap()->lock(), req.is_mutator_alloc()); + + // OldCollector: verify old generation has room before attempting allocation. + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) { + if (!req.is_promotion() && !ShenandoahHeap::heap()->old_generation()->can_allocate(req)) { + return nullptr; + } + } + + bool boundary_changed = false; + size_t min_req_words = req.is_lab_alloc() ? req.min_size() : req.size(); + // Fast path: try the cached alloc region first. + if (_alloc_region != nullptr) { + constexpr ShenandoahAffiliation affiliation = + (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) ? OLD_GENERATION : YOUNG_GENERATION; + assert(!_alloc_region->is_trash() && _alloc_region->affiliation() == affiliation && + _free_set->membership(_alloc_region->index()) == PARTITION, + "Cached alloc region %zu must remain a non-trash member of this partition until the free set is rebuilt", + _alloc_region->index()); + HeapWord* result = nullptr; + size_t ac_words = _alloc_region->free() >> LogHeapWordSize; + // A region is only ever cached while it has at least PLAB::min_size of capacity, and its + // free space shrinks only via allocate_in (which retires and clears it below that threshold). + // So the cached alloc region always has usable capacity here: use it when it can satisfy this + // request, otherwise keep it cached for a smaller future request and fall through. + assert(ac_words >= PLAB::min_size(), + "Cached alloc region %zu must keep at least PLAB::min_size capacity, has %zu words", + _alloc_region->index(), ac_words); + if (ac_words >= min_req_words) { + result = allocate_in(_alloc_region, req, boundary_changed); + } + if (result != nullptr) { + in_new_region = false; + _free_set->notify_allocation(PARTITION, false, boundary_changed); + return result; + } + } + + // Ask FreeSet to find a suitable region. + ShenandoahHeapRegion* r = _free_set->find_region_for_alloc(min_req_words, in_new_region); + // Collector partitions can overflow into Mutator partition. + if constexpr (PARTITION != ShenandoahFreeSetPartitionId::Mutator) { + if (r == nullptr && ShenandoahEvacReserveOverflow) { + r = _free_set->steal_from_mutator(PARTITION, req); + if (r != nullptr) { + assert(r->is_empty(), "Stolen region must be empty"); + in_new_region = true; + } + } + } + + if (r != nullptr) { + HeapWord* result = allocate_in(r, req, boundary_changed); + if (in_new_region) { + _free_set->mark_region_used(PARTITION); + boundary_changed = true; + } + _free_set->notify_allocation(PARTITION, in_new_region, boundary_changed); + return result; + } + + // Every path that mutates a partition boundary (allocate_in retire, new region, steal) returns + // above, so reaching here means no allocation and no boundary change. + return nullptr; +} + +template +HeapWord* ShenandoahPartitionAllocator::allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& boundary_changed) { + assert(_free_set->alloc_capacity(r) > 0, "Performance: should avoid full regions on this path: %zu", r->index()); + + HeapWord* result = nullptr; + + // Perform the actual allocation: LABs may be shrunk to fit. + if (req.is_lab_alloc()) { + size_t adjusted_size = req.size(); + size_t free = align_down(r->free() >> LogHeapWordSize, MinObjAlignment); + if (adjusted_size > free) { + adjusted_size = free; + } + assert(adjusted_size >= req.min_size(), + "Caller must ensure region has at least min_size capacity: free=%zu, min_size=%zu", + free, req.min_size()); + result = r->allocate(adjusted_size, req); + req.set_actual_size(adjusted_size); + } else { + size_t size = req.size(); + result = r->allocate(size, req); + req.set_actual_size(size); + } + assert(result != nullptr, "Allocation must succeed, region free: %zu, request minimal size: %zu", + r->free(), req.is_lab_alloc() ? req.min_size() : req.size()); + + // Update partition used bytes after allocation + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::Mutator) { + assert(req.is_young(), "Mutator allocations always come from young generation."); + _free_set->increase_partition_used(PARTITION, req.actual_size() * HeapWordSize); + } else { + assert(req.is_gc_alloc(), "Should be gc_alloc since req wasn't mutator alloc"); + // For GC allocations, we advance update_watermark because the objects relocated into this memory during + // evacuation are not updated during evacuation. For both young and old regions, it is essential that all + // PLABs be made parsable at the end of evacuation. This is enabled by retiring all plabs at end of evacuation. + r->set_update_watermark(r->top()); + _free_set->increase_partition_used(PARTITION, (req.actual_size() + req.waste()) * HeapWordSize); + } + + // Retire the region if remaining capacity is too small for any future PLAB. + if ((r->free() >> LogHeapWordSize) < PLAB::min_size()) { + size_t idx = r->index(); + size_t waste_bytes = _free_set->retire_region(PARTITION, idx, r->used()); + boundary_changed = true; + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::Mutator) { + if (waste_bytes > 0) { + req.set_waste(waste_bytes / HeapWordSize); + } + } + if (_alloc_region == r) { + _alloc_region = nullptr; + } + } else if (_alloc_region == nullptr) { + // Region still has usable capacity — cache it for next allocation. + _alloc_region = r; + } + + return result; +} + +// Explicit template instantiations for all partitions. +template class ShenandoahPartitionAllocator; +template class ShenandoahPartitionAllocator; +template class ShenandoahPartitionAllocator; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp new file mode 100644 index 00000000000..dfcaf2b7097 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp @@ -0,0 +1,66 @@ +/* + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPARTITIONALLOCATOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPARTITIONALLOCATOR_HPP + +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "memory/allocation.hpp" + +// ShenandoahPartitionAllocator is the serial (lock-based) partition allocator. +// It uses ShenandoahFreeSet APIs to find regions and performs allocation within them +// under the heap lock. Templated on partition ID so that partition-specific behavior +// (overflow stealing for Collector/OldCollector) is resolved at compile time. +template +class ShenandoahPartitionAllocator : public CHeapObj { + +private: + ShenandoahFreeSet* const _free_set; + + // Cached allocation region with remaining capacity from the last allocation in + // this partition. Checked first on the next request to skip a FreeSet scan. + // Cleared when retired by allocate_in or by release_alloc_region. + ShenandoahHeapRegion* _alloc_region; + + // Allocate within a single region; the caller must guarantee the region has enough free + // capacity for the request. Handles LAB sizing, updates partition accounting via + // ShenandoahFreeSet, and retires the region if remaining capacity drops below PLAB::min_size(). + // boundary_changed is set to true if the region is retired or otherwise mutates the partition + // boundary; it is never reset to false. + HeapWord* allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& boundary_changed); + +public: + ShenandoahPartitionAllocator(ShenandoahFreeSet* free_set); + + // Allocate from this partition. Returns nullptr if partition cannot satisfy the request. + HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); + + // Drop the cached alloc region. Must be called before the free set is rebuilt, + // since rebuild can change region affiliation/membership and invalidate the cache. + void release_alloc_region() { _alloc_region = nullptr; } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARTITIONALLOCATOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index bc52d755139..dfb42e0b76f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -106,8 +106,10 @@ class outputStream; " CE: ") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_update_card_table, "Concurrent Update Cards") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_final_roots, "Concurrent Final Roots") \ - SHENANDOAH_WORKER_PHASE_DEF(f, promote_in_place, " Promote Regions", \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated, "Complete Abbreviated Cycle") \ + SHENANDOAH_WORKER_PHASE_DEF(f, complete_abbreviated_promote_in_place, " Promote Regions", \ " PIP: ") \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated_update_region_ages, " Update Region Ages") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, final_verify_gross, "Pause Final Verify (G)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, final_verify, "Pause Final Verify (N)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, init_update_refs_gross, "Pause Init Update Refs (G)") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp new file mode 100644 index 00000000000..35aa297e629 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp @@ -0,0 +1,77 @@ +/* + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPREFETCH_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPREFETCH_INLINE_HPP + +// No shenandoahPrefetch.hpp + +#include "memory/allStatic.hpp" +#include "runtime/prefetch.inline.hpp" + +// Utility to centralize prefetching decisions. +// +// Prefetching needs to strike the balance between the latency savings +// from upcoming accesses and the excess memory throughput for accesses +// that are prefetched but are never used. +// +// A common access pattern for the object in hot GC code is: +// [mark word] // sometimes, for forwarding pointer accesses +// [klass word] // very often, to discover object type +// ... +// [oop field N] // often, to traverse the heap or fix references +// +// Prefetches work on cache line granularity, so we can pick and choose +// good static offsets at which to prefetch. It also frees us from +// polling mark/klass word offsets at runtime. +// +// It stands to reason that prefetching at zero is most beneficial. +// Since it is almost guaranteed to be used by future accesses, there is +// little downside. For objects that are fully within the cache line, +// that zero-prefetch also picks up oop fields nicely. +// +// Experiments suggest it is also important to handle the case when +// object crosses the cache line. In this case, zero-prefetch is likely +// to miss the oop fields cache line. In extreme case, it can prefetch only +// the mark word, leaving klass word unprefetched. We can prefetch +// the full next cache line to deal with this case, but it is wasteful, +// especially on platforms with very large cache lines. +// +// Therefore, the second prefetch is done at some small offset to balance +// the crossing case. If second prefetch hits the same cache line as the +// first one, there is little downside. This also works automagically with +// platforms with larger cache line sizes, as both prefetches would converge. +// If prefetch hits another cache line, it likely means the object crosses +// the cache line, and that the second prefetch is profitable. +// +class ShenandoahPrefetch : AllStatic { +public: + static void prefetch(oop obj) { + void* addr = obj->base_addr(); + Prefetch::read(addr, 0); + Prefetch::read(addr, 32); + } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPREFETCH_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index 28094a1d57d..8823e7e4af7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -116,23 +116,21 @@ void ShenandoahRegulatorThread::regulator_sleep() { // Wait before performing the next action. If allocation happened during this wait, // we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle, // back off exponentially. - double before_sleep_time = _most_recent_wake_time; if (ShenandoahHeap::heap()->has_changed()) { _sleep = ShenandoahControlIntervalMin; - } else if ((before_sleep_time - _last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ + } else if ((_most_recent_wake_time - _last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ _sleep = MIN2(ShenandoahControlIntervalMax, MAX2(1u, _sleep * 2)); - _last_sleep_adjust_time = before_sleep_time; + _last_sleep_adjust_time = _most_recent_wake_time; } SuspendibleThreadSetLeaver leaver; + const double before_sleep_time = os::elapsedTime(); os::naked_short_sleep(_sleep); - double wake_time = os::elapsedTime(); - _most_recent_period = wake_time - _most_recent_wake_time; - _most_recent_wake_time = wake_time; + _most_recent_wake_time = os::elapsedTime(); _young_heuristics->update_should_start_query_times(_most_recent_wake_time, double(_sleep) / 1000.0); if (LogTarget(Debug, gc, thread)::is_enabled()) { double elapsed = _most_recent_wake_time - before_sleep_time; - double hiccup = elapsed - double(_sleep); + double hiccup = elapsed - double(_sleep) / 1000.0; if (hiccup > 0.001) { log_debug(gc, thread)("Regulator hiccup time: %.3fs", hiccup); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp index cc41bc2c65b..62a5dcce9df 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp @@ -82,7 +82,6 @@ class ShenandoahRegulatorThread: public ConcurrentGCThread { // duration of planned regulator sleep period, in ms uint _sleep; double _most_recent_wake_time; - double _most_recent_period; double _last_sleep_adjust_time; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp index 81c584dfa37..8df2449f8b6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp @@ -67,14 +67,13 @@ ShenandoahStackWatermark::ShenandoahStackWatermark(JavaThread* jt) : OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { if (context != nullptr) { - assert(_heap->is_concurrent_weak_root_in_progress() || + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || _heap->is_concurrent_mark_in_progress(), "Only these two phases"); assert(Thread::current()->is_Worker_thread(), "Unexpected thread passing in context: " PTR_FORMAT, p2i(context)); return reinterpret_cast(context); } else { - if (_heap->is_concurrent_weak_root_in_progress()) { - assert(_heap->is_evacuation_in_progress(), "Nothing to evacuate"); + if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { return &_evac_update_oop_cl; } else if (_heap->is_concurrent_mark_in_progress()) { return &_keep_alive_cl; @@ -87,11 +86,9 @@ OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { void ShenandoahStackWatermark::start_processing_impl(void* context) { NoSafepointVerifier nsv; - ShenandoahHeap* const heap = ShenandoahHeap::heap(); // Process the non-frame part of the thread - if (heap->is_concurrent_weak_root_in_progress()) { - assert(heap->is_evacuation_in_progress(), "Should not be armed"); + if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { // Retire the TLABs, which will force threads to reacquire their TLABs. // This is needed for two reasons. Strong one: new allocations would be with new freeset, // which would be outside the collection set, so no cset writes would happen there. @@ -100,7 +97,7 @@ void ShenandoahStackWatermark::start_processing_impl(void* context) { retire_tlab(); _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); - } else if (heap->is_concurrent_mark_in_progress()) { + } else if (_heap->is_concurrent_mark_in_progress()) { // We need to reset all TLABs because they might be below the TAMS, and we need to mark // the objects in them. Do not let mutators allocate any new objects in their current TLABs. // It is also a good place to resize the TLAB sizes for future allocations. @@ -129,9 +126,8 @@ void ShenandoahStackWatermark::retire_tlab() { void ShenandoahStackWatermark::process(const frame& fr, RegisterMap& register_map, void* context) { OopClosure* oops = closure_from_context(context); assert(oops != nullptr, "Should not get to here"); - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || - heap->is_concurrent_mark_in_progress(), + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || + _heap->is_concurrent_mark_in_progress(), "Only these two phases"); fr.oops_do(oops, &_nm_cl, ®ister_map, DerivedPointerIterationMode::_directly); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp new file mode 100644 index 00000000000..d6e55d06248 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp @@ -0,0 +1,38 @@ +/* + * 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 "gc/shenandoah/shenandoahStripedCounter.hpp" +#include "memory/padded.inline.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/powerOfTwo.hpp" + +ShenandoahStripedCounter::ShenandoahStripedCounter() + : _num_stripes(round_down_power_of_2((uint32_t) MAX2(os::processor_count(), 1))) + , _stripe_mask(_num_stripes - 1) + , _log_num_stripes(log2i_exact(_num_stripes)) { + _stripes = PaddedArray, mtGC>::create_unfreeable(_num_stripes); +} + +ShenandoahStripedCounter::~ShenandoahStripedCounter() { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp new file mode 100644 index 00000000000..ad1086005a6 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp @@ -0,0 +1,79 @@ +/* + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_HPP + +#include "memory/allocation.hpp" +#include "memory/padded.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" + +// A contended-counter optimized for many concurrent writers and infrequent reads. +// Each writer accumulates into a stripe chosen by its thread hash, each on its own cache line to +// avoid false sharing. Stripes are shared when live writers outnumber stripes (num_stripes <= CPU +// count). The value of the counter is always sum(stripes). +// +// Reads (sum) are approximate under concurrent writes and exact when quiescent. +// This counter is monotonic per epoch: add() only increases it; drain() atomically reads and resets +// to begin a new epoch (0), preserving concurrent adds that race with the drain. +class ShenandoahStripedCounter : public CHeapObj { + typedef PaddedEnd> PaddedCounter; + + PaddedCounter* _stripes; // _num_stripes entries + // Number of stripes: a power of two, rounded down from the CPU count. Keeping it a power of two + // lets current_stripe() map a thread hash into range with a mask (& _stripe_mask) instead of a + // modulo on the hot path. + uint32_t const _num_stripes; + uint32_t const _stripe_mask; // _num_stripes - 1 + uint32_t const _log_num_stripes; + + // The stripe this thread uses. + uint32_t current_stripe() const; + +public: + ShenandoahStripedCounter(); + ~ShenandoahStripedCounter(); + + // Add `bytes` to the current stripe of the counter and return the resulting total of the current stripe. + size_t add(size_t bytes); + + // Current total of all stripes of the counter. No reset. + // Approximate under concurrent writes. + size_t sum() const; + + // Current value of the calling thread's own stripe. O(1), no reset. + size_t current_stripe_value() const; + + // Read the total and atomically reset it to zero, returning the amount consumed. + // Concurrent adds racing with the drain accumulate toward the next epoch rather than being lost. + size_t drain(); + + // Number of stripes (a power of two, <= CPU count), and its base-2 log. Exposed so a caller can + // scale a threshold to a per-stripe share with a shift (>> log_num_stripes) instead of a divide. + uint32_t num_stripes() const; + uint32_t log_num_stripes() const; +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp new file mode 100644 index 00000000000..58c40089324 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp @@ -0,0 +1,74 @@ +/* + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_INLINE_HPP + +#include "gc/shenandoah/shenandoahStripedCounter.hpp" + +#include "runtime/thread.hpp" + +inline uint32_t ShenandoahStripedCounter::current_stripe() const { + if (_num_stripes == 1u) { + return 0u; + } + // Per-thread probe into [0, _num_stripes). Hashing the thread pointer spreads threads across + // stripes. This is a pure, stable function of (thread pointer, _num_stripes) + const uintptr_t t = (uintptr_t) Thread::current(); + return (uint32_t) ((t ^ (t >> 20) ^ (t >> 9)) & _stripe_mask); +} + +inline uint32_t ShenandoahStripedCounter::num_stripes() const { + return _num_stripes; +} + +inline uint32_t ShenandoahStripedCounter::log_num_stripes() const { + return _log_num_stripes; +} + +inline size_t ShenandoahStripedCounter::add(const size_t bytes) { + return _stripes[current_stripe()].add_then_fetch(bytes, memory_order_relaxed); +} + +inline size_t ShenandoahStripedCounter::sum() const { + size_t total = 0; + for (uint32_t i = 0; i < _num_stripes; i++) { + total += _stripes[i].load_relaxed(); + } + return total; +} + +inline size_t ShenandoahStripedCounter::current_stripe_value() const { + return _stripes[current_stripe()].load_relaxed(); +} + +inline size_t ShenandoahStripedCounter::drain() { + size_t total = 0; + for (uint32_t i = 0; i < _num_stripes; i++) { + total += _stripes[i].exchange(0, memory_order_relaxed); + } + return total; +} + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index ad4e29a5cc2..7bdf2d27349 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -49,10 +49,12 @@ public: TASKQUEUE_STATS_ONLY(using taskqueue_t::stats;) // Push task t into the queue. Returns true on success. - inline bool push(E t); + ALWAYSINLINE + bool push(E t); // Attempt to pop from the queue. Returns true on success. - inline bool pop(E &t); + ALWAYSINLINE + bool pop(E &t); inline void clear(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp index 9fa4fabc1c7..0f01425f3e9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp @@ -47,7 +47,7 @@ bool BufferedOverflowTaskQueue::pop(E &t) { } template -inline bool BufferedOverflowTaskQueue::push(E t) { +bool BufferedOverflowTaskQueue::push(E t) { if (_buf_empty) { _elem = t; _buf_empty = false; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 5df88c0fc0a..19c33c77b26 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -388,7 +388,7 @@ public: _used(0), _committed(0), _garbage(0), _regions(0), _humongous_waste(0), _trashed_regions(0), _trashed_used(0) { _region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - // Retired regions are not necessarily filled, thouugh their remnant memory is considered used. + // Retired regions are not necessarily filled, though their remnant memory is considered used. _min_free_size = PLAB::min_size() * HeapWordSize; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp index 2a1036b3728..54572405f5f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp @@ -39,7 +39,7 @@ ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues) : void ShenandoahYoungGeneration::set_concurrent_mark_in_progress(bool in_progress) { ShenandoahHeap* heap = ShenandoahHeap::heap(); heap->set_concurrent_young_mark_in_progress(in_progress); - if (is_bootstrap_cycle() && in_progress && !heap->is_prepare_for_old_mark_in_progress()) { + if (is_old_marking_active() && in_progress && !heap->is_prepare_for_old_mark_in_progress()) { // This is not a bug. When the bootstrapping marking phase is complete, // the old generation marking is still in progress, unless it's not. // In the case that old-gen preparation for mixed evacuation has been @@ -79,7 +79,7 @@ bool ShenandoahYoungGeneration::is_concurrent_mark_in_progress() { void ShenandoahYoungGeneration::reserve_task_queues(uint workers) { ShenandoahGeneration::reserve_task_queues(workers); - if (is_bootstrap_cycle()) { + if (is_old_marking_active()) { _old_gen_task_queues->reserve(workers); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp index 6f514c8a322..6050c78a08c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp @@ -69,7 +69,7 @@ public: // Returns true if the young generation is configured to enqueue old // oops for the old generation mark queues. - bool is_bootstrap_cycle() const { + bool is_old_marking_active() const { return _old_gen_task_queues != nullptr; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 3647a818490..d76348b030a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -461,6 +461,12 @@ product(bool, ShenandoahAllocFailureALot, false, DIAGNOSTIC, \ "Testing: make lots of artificial allocation failures.") \ \ + product(uintx, ShenandoahPinRegionRate, 0, DIAGNOSTIC, \ + "Testing: rate at which to artificially pin regions. Expressed " \ + "as N in 1000 chances for a region to be randomly pinned per " \ + "injection attempt.") \ + range(0, 1000) \ + \ product(uintx, ShenandoahCoalesceChance, 0, DIAGNOSTIC, \ "Testing: Abandon remaining mixed collections with this " \ "likelihood. Following each mixed collection, abandon all " \ @@ -470,11 +476,6 @@ "evacuated.") \ range(0, 100) \ \ - product(intx, ShenandoahMarkScanPrefetch, 32, EXPERIMENTAL, \ - "How many objects to prefetch ahead when traversing mark bitmaps."\ - "Set to 0 to disable prefetching.") \ - range(0, 256) \ - \ product(uintx, ShenandoahMarkLoopStride, 1000, EXPERIMENTAL, \ "How many items to process during one marking iteration before " \ "checking for cancellation, yielding, etc. Larger values improve "\ diff --git a/src/hotspot/share/gc/z/zBarrierSet.cpp b/src/hotspot/share/gc/z/zBarrierSet.cpp index f6f99672886..c5d6fc5a9b1 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.cpp +++ b/src/hotspot/share/gc/z/zBarrierSet.cpp @@ -251,13 +251,14 @@ void ZBarrierSet::on_thread_destroy(Thread* thread) { } void ZBarrierSet::on_thread_attach(Thread* thread) { + BarrierSet::on_thread_attach(thread); + // Set thread local masks ZThreadLocalData::set_load_bad_mask(thread, ZPointerLoadBadMask); ZThreadLocalData::set_load_good_mask(thread, ZPointerLoadGoodMask); ZThreadLocalData::set_mark_bad_mask(thread, ZPointerMarkBadMask); ZThreadLocalData::set_store_bad_mask(thread, ZPointerStoreBadMask); ZThreadLocalData::set_store_good_mask(thread, ZPointerStoreGoodMask); - ZThreadLocalData::set_nmethod_disarmed(thread, ZPointerStoreGoodMask); if (thread->is_Java_thread()) { JavaThread* const jt = JavaThread::cast(thread); StackWatermark* const watermark = new ZStackWatermark(jt); diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index a439b3a167b..6e89c5a1032 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.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 @@ -30,7 +30,6 @@ #include "gc/z/zLock.inline.hpp" #include "gc/z/zNMethod.hpp" #include "gc/z/zResurrection.inline.hpp" -#include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "logging/log.hpp" #include "runtime/icache.hpp" @@ -98,10 +97,6 @@ int* ZBarrierSetNMethod::disarmed_guard_value_address() const { return (int*)ZPointerStoreGoodMaskLowOrderBitsAddr; } -ByteSize ZBarrierSetNMethod::thread_disarmed_guard_value_offset() const { - return ZThreadLocalData::nmethod_disarmed_offset(); -} - oop ZBarrierSetNMethod::oop_load_no_keepalive(const nmethod* nm, int index) { return ZNMethod::oop_load_no_keepalive(nm, index); } diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp index c7bbe35e17d..304be1f0a88 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.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 @@ -36,7 +36,6 @@ protected: public: uintptr_t color(nmethod* nm); - virtual ByteSize thread_disarmed_guard_value_offset() const; virtual int* disarmed_guard_value_address() const; virtual oop oop_load_no_keepalive(const nmethod* nm, int index); diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index d69475e62a3..1c2a4078904 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -642,7 +642,7 @@ private: const zaddress to_addr = _forwarding->insert(from_addr, allocated_addr, &cursor); if (to_addr != allocated_addr) { // Already relocated, undo allocation - _allocator->undo_alloc_object(to_page, allocated_addr, size); + _allocator->undo_alloc_object(to_page, to_addr, size); increase_other_forwarded(size); } diff --git a/src/hotspot/share/gc/z/zStackWatermark.cpp b/src/hotspot/share/gc/z/zStackWatermark.cpp index 4a50dea0cec..de57ea974f3 100644 --- a/src/hotspot/share/gc/z/zStackWatermark.cpp +++ b/src/hotspot/share/gc/z/zStackWatermark.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 @@ -23,6 +23,7 @@ #include "gc/z/zAddress.hpp" #include "gc/z/zBarrier.inline.hpp" +#include "gc/z/zBarrierSet.hpp" #include "gc/z/zGeneration.inline.hpp" #include "gc/z/zStackWatermark.hpp" #include "gc/z/zStoreBarrierBuffer.hpp" @@ -189,7 +190,9 @@ void ZStackWatermark::start_processing_impl(void* context) { ZThreadLocalData::set_mark_bad_mask(_jt, ZPointerMarkBadMask); ZThreadLocalData::set_store_bad_mask(_jt, ZPointerStoreBadMask); ZThreadLocalData::set_store_good_mask(_jt, ZPointerStoreGoodMask); - ZThreadLocalData::set_nmethod_disarmed(_jt, ZPointerStoreGoodMask); + + // Update thread-local nmethod disarmed guard value + BarrierSet::barrier_set()->barrier_set_nmethod()->set_thread_disarmed_guard_value(_jt); // Retire TLAB if (ZGeneration::young()->is_phase_mark() || ZGeneration::old()->is_phase_mark()) { diff --git a/src/hotspot/share/gc/z/zThreadLocalData.hpp b/src/hotspot/share/gc/z/zThreadLocalData.hpp index a141fd8f83a..297d57c2cfe 100644 --- a/src/hotspot/share/gc/z/zThreadLocalData.hpp +++ b/src/hotspot/share/gc/z/zThreadLocalData.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 @@ -39,7 +39,6 @@ private: uintptr_t _mark_bad_mask; uintptr_t _store_good_mask; uintptr_t _store_bad_mask; - uintptr_t _nmethod_disarmed; ZStoreBarrierBuffer* _store_barrier_buffer; ZMarkThreadLocalStacks _mark_stacks[2]; zaddress_unsafe* _invisible_root; @@ -50,7 +49,6 @@ private: _mark_bad_mask(0), _store_good_mask(0), _store_bad_mask(0), - _nmethod_disarmed(0), _store_barrier_buffer(new ZStoreBarrierBuffer()), _mark_stacks(), _invisible_root(nullptr) {} @@ -92,10 +90,6 @@ public: data(thread)->_store_good_mask = mask; } - static void set_nmethod_disarmed(Thread* thread, uintptr_t value) { - data(thread)->_nmethod_disarmed = value; - } - static ZMarkThreadLocalStacks* mark_stacks(Thread* thread, ZGenerationId id) { return &data(thread)->_mark_stacks[(int)id]; } @@ -134,10 +128,6 @@ public: return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _store_good_mask); } - static ByteSize nmethod_disarmed_offset() { - return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _nmethod_disarmed); - } - static ByteSize store_barrier_buffer_offset() { return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _store_barrier_buffer); } diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index 23618cb037e..6c555f0c008 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.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 @@ -262,8 +262,8 @@ class AbstractInterpreter: AllStatic { #endif // Local values relative to locals[n] - static int local_offset_in_bytes(int n) { - return ((frame::interpreter_frame_expression_stack_direction() * n) * stackElementSize); + static int local_offset_in_bytes(int n) { + return -n * stackElementSize; } // access to stacked values according to type: diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp index a41515edfbb..58d6c029bc1 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp @@ -486,8 +486,9 @@ void JfrConfigureFlightRecorderDCmd::print_help(outputStream* out, bool startup) out->print_cr(" The option redact-argument is best-effort and applies only to"); out->print_cr(" command-line arguments in the jdk.JVMInformation event and to"); out->print_cr(" the java.command system property in the jdk.InitialSystemProperty"); - out->print_cr(" event. Other events, such as jdk.ProcessStart (child processes),"); - out->print_cr(" are not redacted."); + out->print_cr(" event, and to matching command-line argument text in the values"); + out->print_cr(" of jdk.InitialEnvironmentVariable events. Other events, such as"); + out->print_cr(" jdk.ProcessStart (child processes), are not redacted."); out->print_cr(""); out->print_cr(" If the redact-argument option is not specified, the following"); out->print_cr(" filters are used by default:"); diff --git a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp index 5ace0d0fae4..652320c9904 100644 --- a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp +++ b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp @@ -29,6 +29,7 @@ #include "logging/logMessage.hpp" #include "runtime/arguments.hpp" #include "runtime/flags/jvmFlag.hpp" +#include "runtime/javaThread.hpp" #include "runtime/os.hpp" #include "runtime/vm_version.hpp" #include "services/diagnosticArgument.hpp" @@ -46,17 +47,20 @@ using StringFlag = JfrRedactedEvents::StringFlag; using StringKeyValueArray = GrowableArray*; static const char REDACTED[] = "[REDACTED]"; +static const char REDACTED_MARKER = (char)0xFF; static const char DELIMITER[] = " "; +static const char REDACT_ARGUMENT[] = "redact-argument"; static const char REDACT_ARGUMENT_EQUAL[] = "redact-argument="; static const size_t REDACTED_LENGTH = sizeof(REDACTED) -1; static const size_t DELIMITER_LENGTH = sizeof(DELIMITER) -1; -static const size_t REDACT_ARGUMENT_EQUAL_LENGTH = sizeof(REDACT_ARGUMENT_EQUAL) -1; +static const size_t REDACT_ARGUMENT_LENGTH = sizeof(REDACT_ARGUMENT) -1; String* JfrRedactedEvents::_redacted_java_command_line = nullptr; String* JfrRedactedEvents::_redacted_jvm_command_line = nullptr; String* JfrRedactedEvents::_redacted_flags_command_line = nullptr; String* JfrRedactedEvents::_redacted_flight_recorder_options = nullptr; +String* JfrRedactedEvents::_redacted_flight_recorder_options_with_marker = nullptr; StringKeyValueArray JfrRedactedEvents::_initial_environment_variables = nullptr; StringKeyValueArray JfrRedactedEvents::_initial_system_properties = nullptr; @@ -71,6 +75,10 @@ bool JfrRedactedEvents::_initialized = false; bool JfrRedactedEvents::set_argument_filter(const char* filters) { assert (_argument_filters == nullptr, "invariant"); assert (filters != nullptr, "invariant"); + if (strcmp(filters, "*") != 0 && strcmp(filters, "none") != 0) { + _redacted_arguments = new StringArray(); + _redacted_arguments->add(filters); + } _argument_filters = new StringArray(); return append_filters(_argument_filters, true, filters); } @@ -139,9 +147,8 @@ bool JfrRedactedEvents::append_filters(StringArray* target, bool argument, const } if (filters[0] == '\0') { LogMessage(jfr, redact) msg; - msg.warning("Default redaction filters are replaced. Specify:"); - msg.warning("-XX:FlightRecorderOptions:%s=none to disable filters without a warning.", option_name); - return true; + msg.error("Specify -XX:FlightRecorderOptions:%s=none to disable filters completely.", option_name); + return false; } if (strcmp(filters, "none") == 0) { return true; @@ -187,6 +194,67 @@ char* JfrRedactedEvents::new_redacted_text() { return result; } +void JfrRedactedEvents::redact(String* scratch_string, const char* target, const String* redaction) { + if (strchr(redaction->text(), REDACTED_MARKER)) { + return; + } + const char* position = target; + while (true) { + const char* sensitive = strstr(position, redaction->text()); + if (sensitive == nullptr) { + return; + } + size_t index = (size_t)(sensitive - target); + for (size_t i = 0; i < redaction->length(); i++) { + scratch_string->set(index + i, REDACTED_MARKER); + } + position = sensitive + 1; + } +} + +String* JfrRedactedEvents::redact_environment_variable_value(const char* value) { + if (strchr(value, REDACTED_MARKER)) { + return new String(REDACTED); + } + bool changed = false; + String* input = new String(value); + if (_redacted_flight_recorder_options_with_marker != nullptr) { + size_t length = strlen(FlightRecorderOptions); + while (const char* start = strstr(input->text(), FlightRecorderOptions)) { + changed = true; + const char* end = start + length; + stringStream s; + s.write(input->text(), start - input->text()); + s.write(_redacted_flight_recorder_options_with_marker->text(), _redacted_flight_recorder_options_with_marker->length()); + s.write(end, strlen(end)); + String* result = new String(s.base()); + delete input; + input = result; + } + } + String* scratch_string = new String(input->text()); + for (int i = 0; i < _redacted_arguments->length(); i++) { + redact(scratch_string, input->text(), _redacted_arguments->at(i)); + } + stringStream result; + bool inside_redaction = false; + for (size_t i = 0; i < scratch_string->length(); i++) { + if (scratch_string->at(i) == REDACTED_MARKER) { + changed = true; + if (!inside_redaction) { + result.print(REDACTED); + } + inside_redaction = true; + } else { + result.put(scratch_string->at(i)); + inside_redaction = false; + } + } + delete scratch_string; + delete input; + return changed ? new String(result.base()) : nullptr; +} + bool JfrRedactedEvents::emit_initial_environment_variables(bool log) { if (_initial_environment_variables == nullptr) { ensure_initialized(); @@ -207,6 +275,16 @@ bool JfrRedactedEvents::emit_initial_environment_variables(bool log) { if (log) { log_debug(jfr, redact)("Redacted initial environment variable named '%s'", key->text()); } + } else { + String* redacted_value = redact_environment_variable_value(value); + if (redacted_value != nullptr) { + if (log) { + log_debug(jfr, redact)("Redacted argument in initial environment variable value named '%s'", key->text()); + } + _initial_environment_variables->append(new StringKeyValue(key, redacted_value->text())); + delete redacted_value; + continue; + } } _initial_environment_variables->append(new StringKeyValue(key, value)); } @@ -262,6 +340,9 @@ bool JfrRedactedEvents::match_flag(const char* flag_name, const char* arg) { if (flag_name == nullptr || arg == nullptr) { return false; } + if (strncmp(arg, "-XX:", 4) == 0) { + arg += 4; + } while (*flag_name) { if (*arg != *flag_name) { return false; @@ -346,6 +427,34 @@ void JfrRedactedEvents::emit_jvm_information(bool log) { } } +// Method assumes that FlightRecorderOptions has been successfully parsed during startup +String* JfrRedactedEvents::redact_flight_recorder_options(const char* option, bool marker) { + JavaThread* THREAD = JavaThread::current(); + const size_t length = strlen(option); + DCmdArgIter iterator(option, length, ','); + while (iterator.next(THREAD)) { + if (strncmp(iterator.key_addr(), REDACT_ARGUMENT, REDACT_ARGUMENT_LENGTH) == 0) { + const char* start = iterator.value_addr(); + const char* end = start + iterator.value_length(); + stringStream result; + result.write(option, start - option); + if (marker) { + result.put(REDACTED_MARKER); + } else { + result.write(REDACTED, REDACTED_LENGTH); + } + result.write(end, option + length - end); + return new String(result.base()); + } + } + if (HAS_PENDING_EXCEPTION) { + DEBUG_ONLY(ShouldNotReachHere();) + CLEAR_PENDING_EXCEPTION; + return new String(REDACTED); + } + return nullptr; +} + void JfrRedactedEvents::ensure_initialized() { if (_initialized) { return; @@ -359,31 +468,12 @@ void JfrRedactedEvents::ensure_initialized() { add_default_filters(_argument_filters, true); } if (FlightRecorderOptions != nullptr) { - if (strstr(FlightRecorderOptions, REDACT_ARGUMENT_EQUAL) != nullptr) { - DCmdIter iterator(FlightRecorderOptions, ','); - stringStream result; - size_t pos = 0; - while(iterator.has_next()) { - CmdLine line = iterator.next(); - const char* start = line.cmd_addr(); - if (strncmp(start, REDACT_ARGUMENT_EQUAL, REDACT_ARGUMENT_EQUAL_LENGTH) == 0) { - result.print(REDACT_ARGUMENT_EQUAL); - result.print(REDACTED); - // Preserve ',' if there are more tokens - pos = iterator.has_next() ? iterator.cursor() - 1 : iterator.cursor(); - } - while (pos < iterator.cursor()) { - result.write(FlightRecorderOptions + pos, 1); - pos++; - } - } - _redacted_flight_recorder_options = new String(result.base()); - } else { - _redacted_flight_recorder_options = new String(FlightRecorderOptions); - } + _redacted_flight_recorder_options = redact_flight_recorder_options(FlightRecorderOptions, false); + _redacted_flight_recorder_options_with_marker = redact_flight_recorder_options(FlightRecorderOptions, true); + } + if (_redacted_arguments == nullptr) { + _redacted_arguments = new StringArray(); } - - _redacted_arguments = new StringArray(); StringArray* java_args = make_java_args_array(); _redacted_java_command_line = redact_command_line(java_args); @@ -396,7 +486,6 @@ void JfrRedactedEvents::ensure_initialized() { StringArray* flags_args = make_jvm_args_array(Arguments::jvm_flags_array(), Arguments::num_jvm_flags()); _redacted_flags_command_line = redact_command_line(flags_args); delete flags_args; - _initialized = true; } @@ -422,8 +511,8 @@ String* JfrRedactedEvents::redact_command_line(StringArray* arguments) { for (int j = arg_index; j < next_index; j++) { result->add(REDACTED); const char* arg = arguments->at(j)->text(); - if (arg != nullptr && strncmp(arg, "-XX:", 4) == 0) { - _redacted_arguments->add(arg + 4); + if (arg != nullptr) { + _redacted_arguments->add(arg); } } arg_index = next_index; @@ -504,15 +593,23 @@ StringArray* JfrRedactedEvents::make_jvm_args_array(char** jvm_args_array, int a return nullptr; } StringArray* result = new StringArray(array_length); - for(int i = 0; i < array_length; i++) { + for (int i = 0; i < array_length; i++) { char* argument = jvm_args_array[i]; - if (_redacted_flight_recorder_options != nullptr && - strncmp(argument, "-XX:FlightRecorderOptions", 25) == 0) { - const char* text = _redacted_flight_recorder_options->text(); - size_t length = _redacted_flight_recorder_options->length(); - // Length must be at least 26 or the JVM will not start. - result->add(new String(argument, 26, text, length)); - continue; + if (strncmp(argument, "-XX:FlightRecorderOptions", 25) == 0) { + size_t length = strlen(argument); + if (length > 25 && + _redacted_flight_recorder_options != nullptr && + strcmp(argument + 26, FlightRecorderOptions) == 0) { + const char* text = _redacted_flight_recorder_options->text(); + // Length must be at least 26 or the JVM will not start. + result->add(new String(argument, 26, text, _redacted_flight_recorder_options->length())); + continue; + } + if (strstr(argument, REDACT_ARGUMENT_EQUAL) != nullptr) { + _redacted_arguments->add(argument); + result->add("-XX:FlightRecorderOptions:[REDACTED]"); + continue; + } } if (strncmp(argument, "-D", 2) == 0) { const char* key_start = argument + 2; @@ -523,6 +620,7 @@ StringArray* JfrRedactedEvents::make_jvm_args_array(char** jvm_args_array, int a bool redact = match_key(_key_filters, key_tmp->text()); delete key_tmp; if (redact) { + _redacted_arguments->add(argument); size_t unsensitive_length = (size_t)(eq - argument) + 1; result->add(new String(argument, unsensitive_length, REDACTED, REDACTED_LENGTH)); continue; @@ -609,6 +707,9 @@ bool JfrRedactedEvents::match_key(StringArray* filters, const char* text) { } bool JfrRedactedEvents::read_file(StringArray* target, const char* filename) { + if (!is_valid_redaction_file(filename)) { + return false; + } FILE* file = os::fopen(filename, "r"); if (file == nullptr) { log_error(jfr, redact)("Failed to open redaction file: %s", filename); @@ -661,3 +762,21 @@ StringArray* JfrRedactedEvents::split(const char* text, char separator) { } return result; } + +bool JfrRedactedEvents::is_valid_redaction_file(const char* filename) { + struct stat st; + int ret = os::stat(filename, &st); + if (ret != 0) { + log_error(jfr, redact)("Failed to access redaction file %s", filename); + return false; + } + if ((st.st_mode & S_IFMT) != S_IFREG) { + log_error(jfr, redact)("Redaction file %s is not a regular file", filename); + return false; + } + if (st.st_size > 1024*1024) { + log_error(jfr, redact)("Redaction file %s is too large (1024 KB).", filename); + return false; + } + return true; +} diff --git a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp index 38c93310365..c3d6fd32cbc 100644 --- a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp +++ b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp @@ -195,6 +195,7 @@ class JfrRedactedEvents: public AllStatic { static String* _redacted_jvm_command_line; static String* _redacted_flags_command_line; static String* _redacted_flight_recorder_options; + static String* _redacted_flight_recorder_options_with_marker; static GrowableArray* _initial_system_properties; static GrowableArray* _initial_environment_variables; static GrowableArray* _string_flags; @@ -205,6 +206,7 @@ class JfrRedactedEvents: public AllStatic { static bool equals_case_insensitive(char a, char b); static bool is_redacted_key(const char* key); static bool is_separator(char c); + static bool is_valid_redaction_file(const char* filename); static bool is_whitespace(char c); static void ensure_initialized(); static StringArray* make_java_args_array(); @@ -216,7 +218,10 @@ class JfrRedactedEvents: public AllStatic { static int match_arguments(StringArray* filter_array, StringArray* arguments, int arg_index); static bool match_key(StringArray* array, const char* text); static bool read_file(StringArray* target, const char* filename); + static void redact(String* scratch_string, const char* target, const String* redaction); + static String* redact_flight_recorder_options(const char* option, bool marker); static String* redact_command_line(StringArray* arguments); + static String* redact_environment_variable_value(const char* value); static StringArray* split(const char* text, char separator); }; diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp index 0a8b3975139..e0d9c090117 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp @@ -32,6 +32,9 @@ #include "jfr/utilities/jfrTryLock.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "logging/log.hpp" +#ifdef COMPILER2 +#include "opto/c2_globals.hpp" +#endif #include "runtime/atomicAccess.hpp" #include "runtime/globals.hpp" #include "runtime/javaThread.inline.hpp" @@ -288,7 +291,19 @@ class OSThreadSampler : public SuspendedThreadTask { public: OSThreadSampler(JavaThread* jt) : SuspendedThreadTask(jt), _result(THREAD_SUSPENSION_ERROR) {} - void request_sample() { run(); } + void request_sample() { +#ifdef COMPILER2 + if (HotCodeHeap) { + JfrMutexTryLock try_lock(SuspendedThreadTask_lock); + if (try_lock.acquired()) { + run(); + } + return; + } +#endif + run(); + } + JfrSampleResult result() const { return _result; } void do_task(const SuspendedThreadTaskContext& context) { diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 15f45d04af3..0e31a371137 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -175,6 +175,7 @@ class outputStream; LOG_TAG(refine) \ LOG_TAG(region) \ LOG_TAG(reloc) \ + LOG_TAG(relocator) \ LOG_TAG(remset) \ LOG_TAG(resolve) \ LOG_TAG(safepoint) \ diff --git a/src/hotspot/share/memory/memoryReserver.cpp b/src/hotspot/share/memory/memoryReserver.cpp index aaee78ab492..3bc9dc17f7d 100644 --- a/src/hotspot/share/memory/memoryReserver.cpp +++ b/src/hotspot/share/memory/memoryReserver.cpp @@ -125,12 +125,13 @@ ReservedSpace MemoryReserver::reserve_memory_special(char* requested_address, size_t size, size_t alignment, size_t page_size, - bool exec) { + bool exec, + MemTag mem_tag) { log_trace(pagesize)("Attempt special mapping: size: " EXACTFMT ", alignment: " EXACTFMT, EXACTFMTARGS(size), EXACTFMTARGS(alignment)); - char* base = os::reserve_memory_special(size, alignment, page_size, requested_address, exec); + char* base = os::reserve_memory_special(size, alignment, page_size, requested_address, mem_tag, exec); if (base != nullptr) { assert(is_aligned(base, alignment), @@ -172,7 +173,7 @@ ReservedSpace MemoryReserver::reserve(char* requested_address, // explicit large pages and these have to be committed up front to ensure // no reservations are lost. do { - ReservedSpace reserved = reserve_memory_special(requested_address, size, alignment, page_size, executable); + ReservedSpace reserved = reserve_memory_special(requested_address, size, alignment, page_size, executable, mem_tag); if (reserved.is_reserved()) { // Successful reservation using large pages. return reserved; diff --git a/src/hotspot/share/memory/memoryReserver.hpp b/src/hotspot/share/memory/memoryReserver.hpp index d13d8d72512..8b19b86b8db 100644 --- a/src/hotspot/share/memory/memoryReserver.hpp +++ b/src/hotspot/share/memory/memoryReserver.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 @@ -42,7 +42,8 @@ class MemoryReserver : AllStatic { size_t size, size_t alignment, size_t page_size, - bool exec); + bool exec, + MemTag mem_tag); public: // Final destination diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index b97ae9ab540..43bd5e452c8 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -593,7 +593,8 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz optimize_for_zero_base)); if (result == nullptr) { - // Fallback: reserve anywhere + // Fallback: we let the OS decide where to place the area, but align (overallocation-and-cut) + // to metaspace reserve alignment (16MB). log_debug(metaspace, map)("Trying anywhere..."); result = os::reserve_memory_aligned(size, Metaspace::reserve_alignment(), mtClass); } @@ -867,8 +868,10 @@ size_t Metaspace::max_allocation_word_size() { // Callers are responsible for checking null. MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type) { - assert(word_size <= Metaspace::max_allocation_word_size(), - "allocation size too large (%zu)", word_size); + if (word_size > Metaspace::max_allocation_word_size()) { + log_warning(gc, metaspace)("allocation size too large (%zu words)", word_size); + return nullptr; + } assert(loader_data != nullptr, "Should never pass around a null loader_data. " "ClassLoaderData::the_null_class_loader_data() should have been used."); @@ -913,7 +916,7 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype); // Allocation failed. - if (is_init_completed()) { + if (is_init_completed() && word_size <= Metaspace::max_allocation_word_size()) { // Only start a GC if the bootstrapping has completed. // Try to clean out some heap memory and retry. This can prevent premature // expansion of the metaspace. diff --git a/src/hotspot/share/nmt/regionsTree.inline.hpp b/src/hotspot/share/nmt/regionsTree.inline.hpp index 793a5c5f1fa..7f1714fb939 100644 --- a/src/hotspot/share/nmt/regionsTree.inline.hpp +++ b/src/hotspot/share/nmt/regionsTree.inline.hpp @@ -32,7 +32,6 @@ template void RegionsTree::visit_committed_regions(const VirtualMemoryRegion& rgn, F func) { position start = (position)rgn.base(); size_t end = reinterpret_cast(rgn.end()) + 1; - size_t comm_size = 0; NodeHelper prev; visit_range_in_order(start, end, [&](Node* node) { diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index e23076a12bf..08ea3699bc2 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -281,7 +281,6 @@ private: bool RegionIterator::next_committed(address& committed_start, size_t& committed_size) { if (end() <= _current_start) return false; - const size_t page_sz = os::vm_page_size(); const size_t current_size = end() - _current_start; if (os::first_resident_in_range(_current_start, current_size, committed_start, committed_size)) { assert(committed_start != nullptr, "Must be"); diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index ca1c46d4095..134f5a93365 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -188,11 +188,7 @@ void CompressedKlassPointers::initialize_for_given_encoding(address addr, size_t calc_lowest_highest_narrow_klass_id(); - // This has already been checked for SharedBaseAddress and if this fails, it's a bug in the allocation code. - if (!set_klass_decode_mode()) { - fatal("base=" PTR_FORMAT " given with shift %d, cannot be used to encode class pointers", - p2i(_base), _shift); - } + initialize_pd(); DEBUG_ONLY(sanity_check_after_initialization();) } @@ -299,20 +295,7 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { calc_lowest_highest_narrow_klass_id(); - // Initialize JIT-specific decoding settings - if (!set_klass_decode_mode()) { - - // Give fatal error if this is a specified address - if (CompressedClassSpaceBaseAddress == (size_t)_base) { - vm_exit_during_initialization( - err_msg("CompressedClassSpaceBaseAddress=" PTR_FORMAT " given with shift %d, cannot be used to encode class pointers", - CompressedClassSpaceBaseAddress, _shift)); - } else { - // If this fails, it's a bug in the allocation code. - fatal("CompressedClassSpaceBaseAddress=" PTR_FORMAT " given with shift %d, cannot be used to encode class pointers", - p2i(_base), _shift); - } - } + initialize_pd(); DEBUG_ONLY(sanity_check_after_initialization();) } diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index fe1ce9e07ae..ff2dd15eb75 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -270,15 +270,8 @@ public: // Returns true if address points into protection zone (for error reporting) static bool is_in_protection_zone(address addr); -#if defined(AARCH64) && !defined(ZERO) - // Check that with the given base, shift and range, aarch64 code can encode and decode the klass pointer. - static bool check_klass_decode_mode(address base, int shift, const size_t range); - // Called after initialization. - static bool set_klass_decode_mode(); -#else - static bool check_klass_decode_mode(address base, int shift, const size_t range) { return true; } - static bool set_klass_decode_mode() { return true; } -#endif + // platform-specific initializations + static void initialize_pd() NOT_AARCH64({}); }; #endif // SHARE_OOPS_COMPRESSEDKLASS_HPP diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index fd1cf1b1457..8161516421e 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2911,6 +2911,16 @@ bool InstanceKlass::can_be_verified_at_dumptime() const { // SystemDictionaryShared::check_verification_constraints() will not work for this class. return false; } + + if (CDSConfig::is_dumping_final_static_archive() && fail_over_verified()) { + // This is a class with version >50 but was verified with the old verifier in the training run, + // which had -XX:+AOTClassLinking. However, we are now in the assembly run with -XX:-AOTClassLinking. + // As SystemDictionaryShared::check_verification_constraints() does not support this case, + // we must exclude this class. + assert(!CDSConfig::is_dumping_aot_linked_classes(), "must be"); + return false; + } + if (super() != nullptr && !super()->can_be_verified_at_dumptime()) { return false; } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 41f176330fa..721a50c73c6 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -339,6 +339,9 @@ class InstanceKlass: public Klass { bool has_localvariable_table() const { return _misc_flags.has_localvariable_table(); } void set_has_localvariable_table(bool b) { _misc_flags.set_has_localvariable_table(b); } + bool fail_over_verified() const { return _misc_flags.fail_over_verified(); } + void set_fail_over_verified() { _misc_flags.set_fail_over_verified(true); } + // field sizes int nonstatic_field_size() const { return _nonstatic_field_size; } void set_nonstatic_field_size(int size) { _nonstatic_field_size = size; } diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index 1709c02a171..84041a3a0e7 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -54,6 +54,7 @@ class InstanceKlassFlags { flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \ flag(has_final_method , 1 << 13) /* True if klass has final method */ \ flag(trust_final_fields , 1 << 14) /* All instance final fields in this class should be trusted */ \ + flag(fail_over_verified , 1 << 15) /* class failed split verification but passed inference verification */ \ /* end of list */ #define IK_FLAGS_ENUM_NAME(name, value) _misc_##name = value, diff --git a/src/hotspot/share/oops/instanceRefKlass.hpp b/src/hotspot/share/oops/instanceRefKlass.hpp index fc219d06739..de7ec6fce9a 100644 --- a/src/hotspot/share/oops/instanceRefKlass.hpp +++ b/src/hotspot/share/oops/instanceRefKlass.hpp @@ -58,6 +58,16 @@ class InstanceRefKlass: public InstanceKlass { public: InstanceRefKlass(); + static InstanceRefKlass* cast(Klass* k) { + return const_cast(cast(const_cast(k))); + } + + static const InstanceRefKlass* cast(const Klass* k) { + assert(k != nullptr, "k should not be null"); + assert(k->is_reference_instance_klass(), "cast to InstanceRefKlass"); + return static_cast(k); + } + // Oop fields (and metadata) iterators // // The InstanceRefKlass iterators also support reference processing. diff --git a/src/hotspot/share/oops/metadata.hpp b/src/hotspot/share/oops/metadata.hpp index d88bd9d087d..bd3f17fa3f4 100644 --- a/src/hotspot/share/oops/metadata.hpp +++ b/src/hotspot/share/oops/metadata.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. * 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,6 +35,11 @@ class Metadata : public MetaspaceObj { NOT_PRODUCT(int _valid;) public: NOT_PRODUCT(Metadata() : _valid(0) {}) + + // We have to keep the vtable alive under LTGC dead-section removal/LTO + // for serviceability tests to work. + // This can be done by linker settings or modifications to the Metadata class. + NOT_PRODUCT(bool is_valid() const { return _valid == 0; }) int identity_hash() { return (int)(uintptr_t)this; } diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index d0ddbe8dfe6..3ad28190d02 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -369,7 +369,7 @@ inline void stackChunkOopDesc::copy_from_stack_to_chunk(intptr_t* from, intptr_t assert(to >= start_address(), "Chunk underflow"); assert(to + size <= end_address(), "Chunk overflow"); -#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) || defined(ZERO) +#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) || defined(ZERO) // Suppress compilation warning-as-error on unimplemented architectures // that stub out arch-specific methods. Some compilers are smart enough // to figure out the argument is always null and then warn about it. @@ -388,7 +388,7 @@ inline void stackChunkOopDesc::copy_from_chunk_to_stack(intptr_t* from, intptr_t assert(from >= start_address(), ""); assert(from + size <= end_address(), ""); -#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) || defined(ZERO) +#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) || defined(ZERO) // Suppress compilation warning-as-error on unimplemented architectures // that stub out arch-specific methods. Some compilers are smart enough // to figure out the argument is always null and then warn about it. diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 2f64482f55b..07b9e907b8f 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -180,6 +180,12 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c return nullptr; } + Node* out_mem = proj_out_or_null(TypeFunc::Memory); + if (can_reshape && out_mem == nullptr) { // dead node? + return NodeSentinel; + } + + Node* base_src = in(ArrayCopyNode::Src); Node* base_dest = in(ArrayCopyNode::Dest); Node* ctl = in(TypeFunc::Control); diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp index 330a8688110..1baf76f00cd 100644 --- a/src/hotspot/share/opto/bytecodeInfo.cpp +++ b/src/hotspot/share/opto/bytecodeInfo.cpp @@ -295,41 +295,43 @@ bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_met return false; } - // don't use counts with -Xcomp - if (UseInterpreter) { - if (!callee_method->has_compiled_code() && - !callee_method->was_executed_more_than(0)) { - set_msg("never executed"); + // accept cold methods in CTW or -Xcomp + if (InlineColdMethods) { + return false; + } + + if (!callee_method->has_compiled_code() && + !callee_method->was_executed_more_than(0)) { + set_msg("never executed"); + return true; + } + + if (is_init_with_ea(callee_method, caller_method, C)) { + // Escape Analysis: inline all executed constructors + return false; + } + + if (MinInlineFrequencyRatio > 0) { + int call_site_count = caller_method->scale_count(profile.count()); + int invoke_count = caller_method->interpreter_invocation_count(); + assert(invoke_count != 0, "require invocation count greater than zero"); + double freq = (double)call_site_count / (double)invoke_count; + // avoid division by 0, set divisor to at least 1 + int cp_min_inv = MAX2(1, CompilationPolicy::min_invocations()); + double min_freq = MAX2(MinInlineFrequencyRatio, 1.0 / cp_min_inv); + + if (freq < min_freq) { + set_msg("low call site frequency"); return true; } - - if (is_init_with_ea(callee_method, caller_method, C)) { - // Escape Analysis: inline all executed constructors - return false; - } - - if (MinInlineFrequencyRatio > 0) { - int call_site_count = caller_method->scale_count(profile.count()); - int invoke_count = caller_method->interpreter_invocation_count(); - assert(invoke_count != 0, "require invocation count greater than zero"); - double freq = (double)call_site_count / (double)invoke_count; - // avoid division by 0, set divisor to at least 1 - int cp_min_inv = MAX2(1, CompilationPolicy::min_invocations()); - double min_freq = MAX2(MinInlineFrequencyRatio, 1.0 / cp_min_inv); - - if (freq < min_freq) { - set_msg("low call site frequency"); - return true; - } - } } return false; } bool InlineTree::is_not_reached(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile) { - if (!UseInterpreter) { - return false; // -Xcomp + if (InlineColdMethods) { + return false; // CTW or -Xcomp } if (profile.count() > 0) { return false; // reachable according to profile @@ -405,7 +407,7 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method, } } - if (!UseInterpreter && + if (InlineColdMethods && is_init_with_ea(callee_method, caller_method, C)) { // Escape Analysis stress testing when running Xcomp: // inline constructors even if they are not reached. diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index dc2a08a3da5..9ff88e8c310 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -601,6 +601,14 @@ "Number of fields in instance limit for scalar replacement") \ range(0, max_jint) \ \ + product(bool, StressEliminateAllocations, false, DIAGNOSTIC, \ + "Randomly fail allocation elimination attempts") \ + \ + product(uint, StressEliminateAllocationsMean, 20, DIAGNOSTIC, \ + "The expected number of elimination checks made until " \ + "a random failure.") \ + range(1, max_juint) \ + \ product(bool, OptimizePtrCompare, true, \ "Use escape analysis to optimize pointers compare") \ \ @@ -789,6 +797,11 @@ "high tier compiler") \ range(0, max_jint) \ \ + product(bool, InlineColdMethods, false, DIAGNOSTIC, \ + "Inline cold methods that would otherwise be rejected due to " \ + "cold profile counters. Useful for compiler testing to expose " \ + "more code to compilers.") \ + \ product(bool, IncrementalInline, true, \ "do post parse inlining") \ \ diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 8209a279cb0..10df25abcb8 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -173,6 +173,10 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) { kit.set_arguments_for_java_call(call); kit.set_edges_for_java_call(call, false, _separate_io_proj); Node* ret = kit.set_results_for_java_call(call, _separate_io_proj); + if (is_late_inline() && !call->is_boxing_method() && ret->is_Proj()) { + // If late inlining for this call happens in a dead part of the graph it can leave a dead loop behind + ret->mark_not_dead_loop_safe(); + } kit.push_node(method()->return_type()->basic_type(), ret); return kit.transfer_exceptions_into_jvms(); } @@ -271,6 +275,10 @@ JVMState* VirtualCallGenerator::generate(JVMState* jvms) { kit.set_arguments_for_java_call(call); kit.set_edges_for_java_call(call, false /*must_throw*/, _separate_io_proj); Node* ret = kit.set_results_for_java_call(call, _separate_io_proj); + if (is_late_inline() && ret->is_Proj()) { + // If late inlining for this call happens in a dead part of the graph it can leave a dead loop behind + ret->mark_not_dead_loop_safe(); + } kit.push_node(method()->return_type()->basic_type(), ret); // Represent the effect of an implicit receiver null_check @@ -709,26 +717,32 @@ void CallGenerator::do_late_inline_helper() { C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded"); } - // Capture any exceptional control flow - GraphKit kit(new_jvms); - - // Find the result object - Node* result = C->top(); - int result_size = method()->return_type()->size(); - if (result_size != 0 && !kit.stopped()) { - result = (result_size == 1) ? kit.pop() : kit.pop_pair(); - } - - if (call->is_CallStaticJava() && call->as_CallStaticJava()->is_boxing_method()) { - result = kit.must_be_not_null(result, false); - } - if (inline_cg()->is_inline()) { C->set_has_loops(C->has_loops() || inline_cg()->method()->has_loops()); C->env()->notice_inlined_method(inline_cg()->method()); } C->set_inlining_progress(true); - C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup + + // Find the result object and capture any exceptional control flow. + GraphKit kit(new_jvms); + Node* result = C->top(); + + assert(!C->do_cleanup(), "already set"); + if (kit.stopped()) { + C->set_do_cleanup(true); // path is dead; needs cleanup + } else { + result = kit.pop_node(method()->return_type()->basic_type()); + if (result != C->top() && !result_not_used) { + if (call->is_CallStaticJava() && + call->as_CallStaticJava()->is_boxing_method()) { + result = kit.must_be_not_null(result, false); + } + // Limit result type propagation until next IGVN cleanup. + const Type* result_type = kit.gvn().type(callprojs.resproj); + result = kit.gvn().transform(new OpaqueParseNode(C, result, result_type)); + } + } + kit.replace_call(call, result, true, do_asserts); } } diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 7bb6b1dcb77..befa208a5e2 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -468,6 +468,11 @@ const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { if (in_type != nullptr && my_type != nullptr) { TypePtr::PTR in_ptr = in_type->ptr(); if (in_ptr == TypePtr::Null) { + // A null input cast to a type that cannot be null (e.g. NotNull) describes + // an impossible value: the join is empty, so the result must be TOP. + if (my_type->join_ptr(TypePtr::Null) == TypePtr::TopPTR) { + return Type::TOP; + } result = in_type; } else if (in_ptr != TypePtr::Constant) { result = my_type->cast_to_ptr_type(my_type->join_ptr(in_ptr)); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 4ac5c31789f..53a72f979db 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -268,6 +268,8 @@ macro(MulD) macro(MulF) macro(MulHiL) macro(UMulHiL) +macro(MulHiLoL) +macro(UMulHiLoL) macro(MulI) macro(MulL) macro(Multi) @@ -286,6 +288,7 @@ macro(OpaqueZeroTripGuard) macro(OpaqueConstantBool) macro(OpaqueInitializedAssertionPredicate) macro(OpaqueTemplateAssertionPredicate) +macro(OpaqueParse) macro(PowD) macro(ProfileBoolean) macro(OrI) @@ -512,6 +515,7 @@ macro(VectorMaskWrapper) macro(VectorMaskCmp) macro(VectorMaskCast) macro(VectorTest) +macro(VectorBitwiseBlend) macro(VectorBlend) macro(VectorRearrange) macro(VectorLoadMask) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 84974ad717f..93d8e4c425d 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -748,7 +748,8 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, if (StressLCM || StressGCM || StressIGVN || StressCCP || StressIncrementalInlining || StressMacroExpansion || StressMacroElimination || StressUnstableIfTraps || - StressBailout || StressLoopPeeling || StressCountedLoop) { + StressBailout || StressLoopPeeling || StressCountedLoop || + StressEliminateAllocations) { initialize_stress_seed(directive); } @@ -2051,10 +2052,13 @@ void Compile::inline_string_calls(bool parse_time) { _late_inlines_pos = _late_inlines.length(); } + assert(!do_cleanup(), "already set"); + while (_string_late_inlines.length() > 0) { CallGenerator* cg = _string_late_inlines.pop(); cg->do_late_inline(); if (failing()) return; + set_do_cleanup(false); // ignore and reset } _string_late_inlines.trunc_to(0); } @@ -2070,10 +2074,13 @@ void Compile::inline_boxing_calls(PhaseIterGVN& igvn) { _late_inlines_pos = _late_inlines.length(); + assert(!do_cleanup(), "already set"); + while (_boxing_late_inlines.length() > 0) { CallGenerator* cg = _boxing_late_inlines.pop(); cg->do_late_inline(); if (failing()) return; + set_do_cleanup(false); // ignore and reset } _boxing_late_inlines.trunc_to(0); @@ -2647,12 +2654,14 @@ void Compile::check_no_dead_use() const { #endif void Compile::inline_vector_reboxing_calls() { + assert(!do_cleanup(), "already set"); if (C->_vector_reboxing_late_inlines.length() > 0) { _late_inlines_pos = C->_late_inlines.length(); while (_vector_reboxing_late_inlines.length() > 0) { CallGenerator* cg = _vector_reboxing_late_inlines.pop(); cg->do_late_inline(); if (failing()) return; + assert(!do_cleanup(), "should not be set"); print_method(PHASE_INLINE_VECTOR_REBOX, 3, cg->call_node()); } _vector_reboxing_late_inlines.trunc_to(0); @@ -2721,13 +2730,11 @@ static uint collect_unique_inputs(Node* n, Unique_Node_List& inputs) { if (is_vector_bitwise_op(n)) { uint inp_cnt = n->is_predicated_vector() ? n->req()-1 : n->req(); if (VectorNode::is_vector_bitwise_not_pattern(n)) { - for (uint i = 1; i < inp_cnt; i++) { - Node* in = n->in(i); - bool skip = VectorNode::is_all_ones_vector(in); - if (!skip && !inputs.member(in)) { - inputs.push(in); - cnt++; - } + assert(n->req() == (n->is_predicated_vector() ? 4 : 3), "must have 2 data inputs"); + Node* opnd = VectorNode::is_all_ones_vector(n->in(1)) ? n->in(2) : n->in(1); + if (!inputs.member(opnd)) { + inputs.push(opnd); + cnt++; } assert(cnt <= 1, "not unary"); } else { @@ -3278,8 +3285,8 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { // DivMod node so the dependency is not lost. divmod->add_prec_from(n); divmod->add_prec_from(d); - d->subsume_by(divmod->div_proj(), this); - n->subsume_by(divmod->mod_proj(), this); + d->subsume_by(divmod->first_proj(), this); + n->subsume_by(divmod->second_proj(), this); } else { // Replace "a % b" with "a - ((a / b) * b)" Node* mult = MulNode::make(d, d->in(2), bt); @@ -3288,6 +3295,24 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { } } +void Compile::handle_mulhi_mul_op(Node* n, bool is_unsigned) { + const int fused_opcode = is_unsigned ? Op_UMulHiLoL : Op_MulHiLoL; + if (!Matcher::has_match_rule(fused_opcode)) { + return; + } + + Node* mul = n->find_similar(Op_MulL, true); + + if (mul == nullptr) { + return; + } + + MulHiLoLNode* mul_hi_lo = is_unsigned ? static_cast(UMulHiLoLNode::make(n)) + : MulHiLoLNode::make(n); + mul->subsume_by(mul_hi_lo->first_proj(), this); + n->subsume_by(mul_hi_lo->second_proj(), this); +} + void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { case Op_Opaque1: // Remove Opaque Nodes before matching @@ -3481,22 +3506,38 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f ResourceMark rm; Unique_Node_List wq; wq.push(n); + + + // When we remove a CastPP, we need to pin all of its transitive users under the control of + // the removed node. The simplest approach is to pin all of the uses of the removed CastPP, + // but it is overly conservative, as an AddP does not really need pinning. As a result, we + // look through those nodes that do not need pinning and only pin memory access nodes under + // n->in(0). for (uint next = 0; next < wq.size(); ++next) { Node *m = wq.at(next); for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { Node* use = m->fast_out(i); - if (use->is_Mem() || use->is_EncodeNarrowPtr()) { + int use_op = use->Opcode(); + if (use->is_CFG() || use->pinned() || // already pinned at the exact control + use->is_Cmp() || use_op == Op_CastP2X || use_op == Op_Conv2B) { // pure computations + continue; + } else if (use->is_EncodeNarrowPtr() || // EncodeP remembers whether its input is nullable, so it must be pinned + use_op == Op_PartialSubtypeCheck || // This accesses its pointer inputs, so it must depend on them being not-null + use->is_Mem() || use->is_memory_access_intrinsic()) { use->ensure_control_or_add_prec(n->in(0)); + } else if (use_op == Op_AddP || + use_op == Op_CastPP || use_op == Op_CheckCastPP || + use_op == Op_CMoveP || use_op == Op_CMoveN || + use_op == Op_DecodeN || use_op == Op_DecodeNKlass || + use_op == Op_VerifyVectorAlignment) { + // Look through use to find memory accesses if use does not need pinning + wq.push(use); } else { - switch(use->Opcode()) { - case Op_AddP: - case Op_DecodeN: - case Op_DecodeNKlass: - case Op_CheckCastPP: - case Op_CastPP: - wq.push(use); - break; - } + // Should have handled all kinds of nodes, verify that we do not unexpectedly arrive + // here + assert(false, "unexpected node %s", use->Name()); + // Be conservative in product and pin the unexpected use + use->ensure_control_or_add_prec(n->in(0)); } } } @@ -3723,6 +3764,14 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f handle_div_mod_op(n, T_LONG, true); break; + case Op_MulHiL: + handle_mulhi_mul_op(n, false); + break; + + case Op_UMulHiL: + handle_mulhi_mul_op(n, true); + break; + case Op_LoadVector: case Op_StoreVector: #ifdef ASSERT @@ -5307,7 +5356,9 @@ void Compile::igv_print_graph_to_network(const char* name, GrowableArraytype(value)->higher_equal(type)) { + precond(type != nullptr); + + if (phase->type(value)->higher_equal(type)) { return value; } Node* result = nullptr; @@ -5315,7 +5366,9 @@ Node* Compile::narrow_value(BasicType bt, Node* value, const Type* type, PhaseGV result = phase->transform(new LShiftINode(value, phase->intcon(24))); result = new RShiftINode(result, phase->intcon(24)); } else if (bt == T_BOOLEAN) { - result = new AndINode(value, phase->intcon(0xFF)); + assert(type == TypeInt::BOOL || type == TypeInt::UBYTE, "unexpected boolean type: %s", Type::str(type)); + Node* mask = phase->intcon(type == TypeInt::BOOL ? 1 : 0xFF); + result = new AndINode(value, mask); } else if (bt == T_CHAR) { result = new AndINode(value,phase->intcon(0xFFFF)); } else { diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 3c2e1c64119..ab36f59a28f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1257,6 +1257,7 @@ public: void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned); + void handle_mulhi_mul_op(Node* n, bool is_unsigned); // Logic cone optimization. void optimize_logic_cones(PhaseIterGVN &igvn); diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index b398ec27b80..1687ff2cade 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1614,12 +1614,6 @@ const Type* ModFloatingNode::Value(PhaseGVN* phase) const { //============================================================================= -DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3) { - init_req(0, c); - init_req(1, dividend); - init_req(2, divisor); -} - DivModNode* DivModNode::make(Node* div_or_mod, BasicType bt, bool is_unsigned) { assert(bt == T_INT || bt == T_LONG, "only int or long input pattern accepted"); @@ -1645,8 +1639,8 @@ DivModINode* DivModINode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); DivModINode* divmod = new DivModINode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1657,8 +1651,8 @@ DivModLNode* DivModLNode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); DivModLNode* divmod = new DivModLNode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1667,11 +1661,11 @@ DivModLNode* DivModLNode::make(Node* div_or_mod) { Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divI_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstI_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modI_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1682,11 +1676,11 @@ Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divL_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modL_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1698,8 +1692,8 @@ UDivModINode* UDivModINode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); UDivModINode* divmod = new UDivModINode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1710,8 +1704,8 @@ UDivModLNode* UDivModLNode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); UDivModLNode* divmod = new UDivModLNode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1720,11 +1714,11 @@ UDivModLNode* UDivModLNode::make(Node* div_or_mod) { Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divI_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstI_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modI_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1735,11 +1729,11 @@ Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divL_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modL_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 2598429716f..366e3fb882d 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -239,36 +239,20 @@ public: //------------------------------DivModNode--------------------------------------- // Division with remainder result. -class DivModNode : public MultiNode { +class DivModNode : public BinaryMultiNode { protected: - DivModNode( Node *c, Node *dividend, Node *divisor ); + DivModNode(Node* ctrl, Node* dividend, Node* divisor) : BinaryMultiNode(ctrl, dividend, divisor) {} public: - enum { - div_proj_num = 0, // quotient - mod_proj_num = 1 // remainder - }; virtual int Opcode() const; - virtual Node* Identity(PhaseGVN* phase) { return this; } - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) { return nullptr; } - virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } - virtual uint hash() const { return Node::hash(); } - virtual bool is_CFG() const { return false; } - virtual uint ideal_reg() const { return NotAMachineReg; } static DivModNode* make(Node* div_or_mod, BasicType bt, bool is_unsigned); - - ProjNode* div_proj() { return proj_out_or_null(div_proj_num); } - ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); } - -private: - virtual bool depends_only_on_test() const { return false; } }; //------------------------------DivModINode--------------------------------------- // Integer division with remainder result. class DivModINode : public DivModNode { public: - DivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + DivModINode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -281,7 +265,7 @@ public: // Long division with remainder result. class DivModLNode : public DivModNode { public: - DivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + DivModLNode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -295,7 +279,7 @@ public: // Unsigend integer division with remainder result. class UDivModINode : public DivModNode { public: - UDivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + UDivModINode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -308,7 +292,7 @@ public: // Unsigned long division with remainder result. class UDivModLNode : public DivModNode { public: - UDivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + UDivModLNode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 3112bb6b169..4f5251f39e1 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -3240,9 +3240,10 @@ Node* GraphKit::maybe_cast_profiled_obj(Node* obj, Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replace) { kill_dead_locals(); // Benefit all the uncommon traps assert( !stopped(), "dead parse path should be checked in callers" ); - assert(!TypePtr::NULL_PTR->higher_equal(_gvn.type(superklass)->is_klassptr()), + const TypeKlassPtr* klass_ptr_type = _gvn.type(superklass)->isa_klassptr(); + assert(klass_ptr_type != nullptr && !TypePtr::NULL_PTR->higher_equal(klass_ptr_type), "must check for not-null not-dead klass in callers"); - + const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve(); // Make the merge point enum { _obj_path = 1, _fail_path, _null_path, PATH_LIMIT }; RegionNode* region = new RegionNode(PATH_LIMIT); @@ -3278,11 +3279,10 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac // Do we know the type check always succeed? bool known_statically = false; - if (_gvn.type(superklass)->singleton()) { - const TypeKlassPtr* superk = _gvn.type(superklass)->is_klassptr(); + if (improved_klass_ptr_type->singleton()) { const TypeKlassPtr* subk = _gvn.type(obj)->is_oopptr()->as_klass_type(); if (subk->is_loaded()) { - int static_res = C->static_subtype_check(superk, subk); + int static_res = C->static_subtype_check(improved_klass_ptr_type, subk); known_statically = (static_res == Compile::SSC_always_true || static_res == Compile::SSC_always_false); } } @@ -3305,7 +3305,11 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac } // Generate the subtype check - Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, superklass); + Node* improved_superklass = superklass; + if (improved_klass_ptr_type != klass_ptr_type && improved_klass_ptr_type->singleton()) { + improved_superklass = makecon(improved_klass_ptr_type); + } + Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, improved_superklass); // Plug in the success path to the general merge in slot 1. region->init_req(_obj_path, control()); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index d371dfb2e32..ef160ac6f1a 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -500,7 +500,7 @@ class GraphKit : public Phase { int n_size = type2size[n_type]; if (n_size == 1) return pop(); else if (n_size == 2) return pop_pair(); - else return nullptr; + else return C->top(); } Node* control() const { return map_not_null()->control(); } diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 347d63ef57c..d4c91d66759 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -669,9 +669,11 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj BoolNode* bol = iff->in(1)->as_Bool(); if (bol->in(1) && bol->in(1)->is_Cmp()) { const CmpNode* cmp = bol->in(1)->as_Cmp(); - // Val is always the lhs of the comparision: val cmp2 - if (cmp->in(1) == val) { - assert(cmp->Opcode() == Op_CmpI, "signed comparison required"); + // Val is always the lhs of the comparision: val CmpI cmp2 + if (cmp->Opcode() == Op_CmpI && cmp->in(1) == val) { + // Only CmpI allowed, assumed by signed logic below. + // We could extend to CmpU in the future, and would + // have to implement unsigned range logic below. const TypeInt* cmp2_t = gvn->type(cmp->in(2))->isa_int(); if (cmp2_t != nullptr) { jint lo = cmp2_t->_lo; diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 16ba829728b..887681233f1 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -22,14 +22,13 @@ * */ -#include "opto/addnode.hpp" #include "opto/intrinsicnode.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" +#include "opto/opcodes.hpp" #include "opto/phaseX.hpp" -#include "utilities/count_leading_zeros.hpp" +#include "opto/rangeinference.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/population_count.hpp" //============================================================================= // Do not match memory edge. @@ -231,171 +230,68 @@ Node* ExpandBitsNode::Identity(PhaseGVN* phase) { return compress_expand_identity(phase, this); } -static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteger* mask_type, int opc, BasicType bt) { - +// Bit expansion is a reverse process of bit compression. It sequentially reads source bits +// starting from LSB and places them at bit positions in result value where corresponding mask bits +// are 1. Thus, bit expansion for non-negative mask value will always generate a +ve value, this is +// because sign bit of result will never be set to 1 as corresponding mask bit is always 0. +static const Type* expand_bits_value(const TypeInteger* mask_type, BasicType bt) { + assert(bt == T_INT || bt == T_LONG, "unexpected BasicType %s", type2name(bt)); jlong hi = bt == T_INT ? max_jint : max_jlong; jlong lo = bt == T_INT ? min_jint : min_jlong; - assert(bt == T_INT || bt == T_LONG, ""); - // Rule 1: Bit compression selects the source bits corresponding to true mask bits, - // packs them and places them contiguously at destination bit positions - // starting from least significant bit, remaining higher order bits are set - // to zero. - - // Rule 2: Bit expansion is a reverse process, which sequentially reads source bits - // starting from LSB and places them at bit positions in result value where - // corresponding mask bits are 1. Thus, bit expansion for non-negative mask - // value will always generate a +ve value, this is because sign bit of result - // will never be set to 1 as corresponding mask bit is always 0. - - // Case A) Constant mask if (mask_type->is_con()) { + // Case A) Constant mask jlong maskcon = mask_type->get_con_as_long(bt); - if (opc == Op_CompressBits) { - // Case A.1 bit compression:- - // For an outlier mask value of -1 upper bound of the result equals - // maximum integral value, for any other mask value its computed using - // following formula - // Result.Hi = 1 << popcount(mask_bits) - 1 + if (maskcon >= 0L) { + // Case A.2.1 constant mask >= 0 + // Result.Hi = mask, optimistically assuming all source bits + // read starting from least significant bit positions are 1. + // Result.Lo = 0, because at least one bit in mask is zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0x7FFFFFFF (constant mask >= 0) + // result.hi = 0x7FFFFFFF + // result.lo = 0 + hi = maskcon; + lo = 0L; + } else { + // Case A.2.2) mask < 0 + // For constant mask strictly less than zero, the maximum result value will be + // the same as the mask value with its sign bit flipped, assuming all source bits + // except the MSB bit are set(one). // - // For mask values other than -1, lower bound of the result is estimated - // as zero, by assuming at least one mask bit is zero and corresponding source - // bit will be masked, hence result of bit compression will always be - // non-negative value. For outlier mask value of -1, assume all source bits - // apart from most significant bit were set to 0, thereby resulting in - // a minimum integral value. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0xEFFFFFFF (constant mask) - // result.hi = 0x7FFFFFFF - // result.lo = 0 - if (maskcon != -1L) { - int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); - hi = right_n_bits(bitcount); - lo = 0L; - } else { - // preserve originally assigned hi (MAX_INT/LONG) and lo (MIN_INT/LONG) values - // for unknown source bits. - assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); - assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); - } - } else { - // Case A.2 bit expansion:- - assert(opc == Op_ExpandBits, ""); - if (maskcon >= 0L) { - // Case A.2.1 constant mask >= 0 - // Result.Hi = mask, optimistically assuming all source bits - // read starting from least significant bit positions are 1. - // Result.Lo = 0, because at least one bit in mask is zero. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0x7FFFFFFF (constant mask >= 0) - // result.hi = 0x7FFFFFFF - // result.lo = 0 - hi = maskcon; - lo = 0L; - } else { - // Case A.2.2) mask < 0 - // For constant mask strictly less than zero, the maximum result value will be - // the same as the mask value with its sign bit flipped, assuming all source bits - // except the MSB bit are set(one). - // - // To compute minimum result value we assume all but last read source bit as zero, - // this is because sign bit of result will always be set to 1 while other bit - // corresponding to set mask bit should be zero. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0xEFFFFFFF (constant mask) - // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF - // result.lo = 0x80000000 - // - hi = maskcon ^ lo; - // lo still retains MIN_INT/LONG. - assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); - } + // To compute minimum result value we assume all but last read source bit as zero, + // this is because sign bit of result will always be set to 1 while other bit + // corresponding to set mask bit should be zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0xEFFFFFFF (constant mask) + // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF + // result.lo = 0x80000000 + // + hi = maskcon ^ lo; + // lo still retains MIN_INT/LONG. + assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); } - } - - // Case B) Non-constant mask. - if (!mask_type->is_con()) { - if ( opc == Op_CompressBits) { - int result_bit_width; - int mask_bit_width = bt == T_INT ? 32 : 64; - if ((mask_type->lo_as_long() < 0L && mask_type->hi_as_long() >= -1L)) { - // Case B.1 The mask value range includes -1, hence we may use all bits, - // the result has the whole value range. - result_bit_width = mask_bit_width; - } else if (mask_type->hi_as_long() < -1L) { - // Case B.2 Mask value range is strictly less than -1, this indicates presence of at least - // one unset(zero) bit in mask value, thus as per Rule 1, bit compression will always - // result in a non-negative value. This guarantees that MSB bit of result value will - // always be set to zero. - result_bit_width = mask_bit_width - 1; - } else { - assert(mask_type->lo_as_long() >= 0, ""); - // Case B.3 Mask value range only includes non-negative values. Since all integral - // types honours an invariant that TypeInteger._lo <= TypeInteger._hi, thus computing - // leading zero bits of upper bound of mask value will allow us to ascertain - // optimistic upper bound of result i.e. all the bits other than leading zero bits - // can be assumed holding 1 value. - jlong clz = count_leading_zeros(mask_type->hi_as_long()); - // Here, result of clz is w.r.t to long argument, hence for integer argument - // we explicitly subtract 32 from the result. - clz = bt == T_INT ? clz - 32 : clz; - result_bit_width = mask_bit_width - clz; - } - // If the number of bits required to for the mask value range is less than the - // full bit width of the integral type, then the MSB bit is guaranteed to be zero, - // thus the compression result will never be a -ve value and we can safely set the - // lower bound of the bit compression to zero. - lo = result_bit_width == mask_bit_width ? lo : 0L; - - assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); - assert(lo == (bt == T_INT ? min_jint : min_jlong) || lo == 0, ""); - - if (src_type->lo_as_long() >= 0) { - // Lemma 1: For strictly non-negative src, the result of the compression will never be - // greater than src. - // Proof: Since src is a non-negative value, its most significant bit is always 0. - // Thus even if the corresponding MSB of the mask is one, the result will be a +ve - // value. There are three possible cases - // a. All the mask bits corresponding to set source bits are unset(zero). - // b. All the mask bits corresponding to set source bits are set(one) - // c. Some mask bits corresponding to set source bits are set(one) while others are unset(zero) - // - // Case a. results into an allzero result, while Case b. gives us the upper bound which is equals source - // value, while for Case c. the result will lie within [0, src] - // - hi = src_type->hi_as_long(); - lo = 0L; - } - - if (result_bit_width < mask_bit_width) { - // Rule 3: - // We can further constrain the upper bound of bit compression if the number of bits - // which can be set(one) is less than the maximum number of bits of integral type. - hi = MIN2(right_n_bits(result_bit_width), hi); - } + } else { + // Case B) Non-constant mask. + jlong max_mask = mask_type->hi_as_long(); + jlong min_mask = mask_type->lo_as_long(); + // Since mask here a range and not a constant value, hence being + // conservative in determining the value range of result. + if (min_mask >= 0L) { + // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, + // if the lower bound of non-constant mask is a non-negative value then result can never + // be greater than the mask. + // Proof: Since lower bound of the mask is a non-negative value, hence most significant + // bit of its entire value must be unset(zero). If all the lower order 'n' source bits + // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals + // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' + // source bits are unset(zero) there by resuling into a zero value. + hi = max_mask; + lo = 0; } else { - assert(opc == Op_ExpandBits, ""); - jlong max_mask = mask_type->hi_as_long(); - jlong min_mask = mask_type->lo_as_long(); - // Since mask here a range and not a constant value, hence being - // conservative in determining the value range of result. - if (min_mask >= 0L) { - // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, - // if the lower bound of non-constant mask is a non-negative value then result can never - // be greater than the mask. - // Proof: Since lower bound of the mask is a non-negative value, hence most significant - // bit of its entire value must be unset(zero). If all the lower order 'n' source bits - // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals - // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' - // source bits are unset(zero) there by resuling into a zero value. - hi = max_mask; - lo = 0; - } else { - // preserve the lo and hi bounds estimated till now. - } + // preserve the lo and hi bounds estimated till now. } } @@ -423,25 +319,12 @@ const Type* CompressBitsNode::Value(PhaseGVN* phase) const { } BasicType bt = bottom_type()->basic_type(); - const TypeInteger* src_type = t1->is_integer(bt); - const TypeInteger* mask_type = t2->is_integer(bt); - int w = bt == T_INT ? 32 : 64; - - // Constant fold if both src and mask are constants. - if (src_type->is_con() && mask_type->is_con()) { - jlong src = src_type->get_con_as_long(bt); - jlong mask = mask_type->get_con_as_long(bt); - jlong res = compress_bits(src, mask, w); - return bt == T_INT ? static_cast(TypeInt::make(res)) : - static_cast(TypeLong::make(res)); + if (bt == T_INT) { + return RangeInference::infer_compress_bits(t1->is_int(), t2->is_int()); + } else { + assert(bt == T_LONG, "unexpected BasicType %s", type2name(bt)); + return RangeInference::infer_compress_bits(t1->is_long(), t2->is_long()); } - - // Result is zero if src is zero irrespective of mask value. - if (src_type == TypeInteger::zero(bt)) { - return TypeInteger::zero(bt); - } - - return bitshuffle_value(src_type, mask_type, Op_CompressBits, bt); } jlong ExpandBitsNode::expand_bits(jlong src, jlong mask, int bit_count) { @@ -482,5 +365,5 @@ const Type* ExpandBitsNode::Value(PhaseGVN* phase) const { return TypeInteger::zero(bt); } - return bitshuffle_value(src_type, mask_type, Op_ExpandBits, bt); + return expand_bits_value(mask_type, bt); } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index adb8ff2dedb..aa467e84dcf 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2407,7 +2407,8 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c Node* adr = make_unsafe_address(base, offset, type, kind == Relaxed); assert(!stopped(), "Inlining of unsafe access failed: address construction stopped unexpectedly"); - if (_gvn.type(base->uncast())->isa_ptr() == TypePtr::NULL_PTR) { + bool is_non_heap_access = (_gvn.type(base->uncast())->isa_ptr() == TypePtr::NULL_PTR); + if (is_non_heap_access) { if (type != T_OBJECT) { decorators |= IN_NATIVE; // off-heap primitive access } else { @@ -2420,6 +2421,8 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c // Can base be null? Otherwise, always on-heap access. bool can_access_non_heap = TypePtr::NULL_PTR->higher_equal(_gvn.type(base)); + assert(!is_non_heap_access || can_access_non_heap, "sanity"); // is_non_heap_access implies can_access_non_heap + if (!can_access_non_heap) { decorators |= IN_HEAP; } @@ -2435,6 +2438,9 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c Compile::AliasType* alias_type = C->alias_type(adr_type); assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here"); + assert((alias_type->index() == Compile::AliasIdxRaw) == + (is_non_heap_access || (can_access_non_heap && alias_type->field() == nullptr)), "wrong alias"); + if (alias_type->adr_type() == TypeInstPtr::KLASS || alias_type->adr_type() == TypeAryPtr::RANGE) { return false; // not supported @@ -2475,10 +2481,16 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c // Figure out the memory ordering. decorators |= mo_decorator_for_access_kind(kind); - if (!is_store && type == T_OBJECT) { - const TypeOopPtr* tjp = sharpen_unsafe_type(alias_type, adr_type); - if (tjp != nullptr) { - value_type = tjp; + if (!is_store) { + if (type == T_OBJECT) { + const TypeOopPtr* tjp = sharpen_unsafe_type(alias_type, adr_type); + if (tjp != nullptr) { + value_type = tjp; + } + } else if (type == T_BOOLEAN) { + if (mismatched || alias_type->index() == Compile::AliasIdxRaw) { + value_type = TypeInt::UBYTE; + } } } @@ -2502,31 +2514,13 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c if (p == nullptr) { // Could not constant fold the load p = access_load_at(heap_base_oop, adr, adr_type, value_type, type, decorators); - // Normalize the value returned by getBoolean in the following cases - if (type == T_BOOLEAN && - (mismatched || - heap_base_oop == top() || // - heap_base_oop is null or - (can_access_non_heap && field == nullptr)) // - heap_base_oop is potentially null - // and the unsafe access is made to large offset - // (i.e., larger than the maximum offset necessary for any - // field access) - ) { - IdealKit ideal = IdealKit(this); -#define __ ideal. - IdealVariable normalized_result(ideal); - __ declarations_done(); - __ set(normalized_result, p); - __ if_then(p, BoolTest::ne, ideal.ConI(0)); - __ set(normalized_result, ideal.ConI(1)); - ideal.end_if(); - final_sync(ideal); - p = __ value(normalized_result); -#undef __ - } } if (type == T_ADDRESS) { p = gvn().transform(new CastP2XNode(nullptr, p)); p = ConvX2UL(p); + } else if (type == T_BOOLEAN) { + // Truncate boolean values returned by unsafe operations. + p = gvn().transform(new AndINode(p, gvn().intcon(0x1))); } // The load node has the control of the preceding MemBarCPUOrder. All // following nodes will have the control of the MemBarCPUOrder inserted at diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 13feeb77947..e7ccefa6855 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -3129,8 +3129,12 @@ Node* LoopLimitNode::Identity(PhaseGVN* phase) { return this; } -// Match increment with optional truncation: -// CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 +// CHAR: (i+1)&0x7fff Note: does NOT work for char cast (0xffff) +// BYTE: ((i+1)<<8)>>8 Note: does NOT work for byte cast (<< 24 >> 24) +// SHORT: ((i+1)<<16)>>16 +// +// Note: in the future, we should fix both the BYTE and the CHAR case, +// to allow proper optimization of byte/char cast truncation. void CountedLoopConverter::TruncatedIncrement::build(Node* expr) { _is_valid = false; @@ -3146,15 +3150,19 @@ void CountedLoopConverter::TruncatedIncrement::build(Node* expr) { const TypeInteger* trunc_t = TypeInteger::bottom(_bt); if (_bt == T_INT) { - // Try to strip (n1 & M) or (n1 << N >> N) from n1. if (n1op == Op_AndI && - n1->in(2)->is_Con() && - n1->in(2)->bottom_type()->is_int()->get_con() == 0x7fff) { - // %%% This check should match any mask of 2**K-1. - t1 = n1; - n1 = t1->in(1); - n1op = n1->Opcode(); - trunc_t = TypeInt::CHAR; + n1->in(2)->is_Con()) { + // Unsigned truncation. + // Pattern: ((i+1) & mask) + jint mask = n1->in(2)->bottom_type()->is_int()->get_con(); + switch (mask) { + case 0x7fff: // Unsigned 15-bit truncation. For historical reasons. + t1 = n1; + n1 = t1->in(1); + n1op = n1->Opcode(); + trunc_t = TypeInt::make_unsigned(0, mask, 0); + break; + } } else if (n1op == Op_RShiftI && n1->in(1) != nullptr && n1->in(1)->Opcode() == Op_LShiftI && @@ -3848,6 +3856,7 @@ const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { Node* region = phi->in(0); assert(n_ctrl == nullptr || n_ctrl == region, "ctrl parameter must be region"); if (region && region != _phase->C->top()) { + // Compute the union over the types of the paths/inputs. for (uint i = 1; i < phi->req(); i++) { Node* val = phi->in(i); Node* use_c = region->in(i); @@ -3858,10 +3867,17 @@ const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { } else { filtered_t = filtered_t->meet(val_t)->is_int(); } + } else { + // We found no constriant, so we have to assume that this path + // is unconstrained, i.e. it could have the whole int range. + filtered_t = TypeInt::INT; } } } } + + // The filtered type may be worse than what we already know + // about n, so take the intersection. const TypeInt* n_t = _phase->igvn().type(n)->is_int(); if (filtered_t != nullptr) { n_t = n_t->join(filtered_t)->is_int(); @@ -3872,6 +3888,8 @@ const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { //------------------------------filtered_type_from_dominators-------------------------------- // Return a possibly more restrictive type for val based on condition control flow of dominators +// Note: we can also return "nullptr", which means "no constraint", and should be interpreted +// as if we returned TypeInt::INT. const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, Node* use_ctrl) { if (val->is_Con()) { return val->bottom_type()->is_int(); @@ -3892,7 +3910,16 @@ const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, No if (rtn_t == nullptr) { rtn_t = if_t; } else { - rtn_t = rtn_t->join(if_t)->is_int(); + const Type* join_t = rtn_t->join(if_t); + if (!join_t->isa_int()) { + // We may have encountered multiple if conditions, that have no + // overlap, and produce an empty/top type. Returning nullptr + // is conservative, it means we do not constrain the type, which + // will just prevent further optimiziations. + assert(join_t->empty(), "top"); + return nullptr; + } + rtn_t = join_t->is_int(); } } } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 9f841d958ec..71a159352a4 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -2105,7 +2105,6 @@ class CountedLoopConverter { bool is_valid() const { return _is_valid; } Node* incr() const { return _incr; } - // Optional truncation for: CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 Node* outer_trunc() const { return _outer_trunc; } // the outermost truncating node (either the & or the final >>) Node* inner_trunc() const { return _inner_trunc; } // the inner truncating node, if applicable (the << in a <> pair) const TypeInteger* trunc_type() const { return _trunc_type; } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 0ee073e8b06..a4d03970fcf 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -931,7 +931,9 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio // We weren't able to find a value for this field, // give up on eliminating this allocation. - if (field_val == nullptr) { + bool force_scalarization_failure = StressEliminateAllocations && + (C->random() % StressEliminateAllocationsMean == 0); + if (field_val == nullptr || force_scalarization_failure) { uint last = sfpt->req() - 1; for (int k = 0; k < j; k++) { sfpt->del_req(last--); @@ -940,13 +942,21 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio #ifndef PRODUCT if (PrintEliminateAllocations) { - if (field != nullptr) { - tty->print("=== At SafePoint node %d can't find value of field: ", sfpt->_idx); - field->print(); - int field_idx = C->get_alias_index(field_addr_type); - tty->print(" (alias_idx=%d)", field_idx); - } else { // Array's element - tty->print("=== At SafePoint node %d can't find value of array element [%d]", sfpt->_idx, j); + tty->print("=== At SafePoint node %d ", sfpt->_idx); + if (field_val == nullptr) { + tty->print_raw("can't find value of "); + + if (field != nullptr) { + tty->print_raw("field: "); + field->print(); + int field_idx = C->get_alias_index(field_addr_type); + tty->print(" (alias_idx=%d)", field_idx); + } else { // Array's element + tty->print("array element [%d]", j); + } + } else { + assert(force_scalarization_failure, "sanity"); + tty->print_raw("forcibly abort elimination"); } tty->print(", which prevents elimination of: "); if (res == nullptr) diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index d2a9250b3ee..57fa2de05df 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -2385,7 +2385,8 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) { break; } case Op_VectorBlend: - case Op_VectorInsert: { + case Op_VectorInsert: + case Op_VectorBitwiseBlend: { Node* pair = new BinaryNode(n->in(1), n->in(2)); n->set_req(1, pair); n->set_req(2, n->in(3)); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 31f4a782247..2453a7ece4e 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -418,15 +418,15 @@ public: static OptoReg::Name inline_cache_reg(); static int inline_cache_reg_encode(); - // Register for DIVI projection of divmodI - static const RegMask& divI_proj_mask(); - // Register for MODI projection of divmodI - static const RegMask& modI_proj_mask(); + // Register for the first projection of an int pair + static const RegMask& firstI_proj_mask(); + // Register for the second projection of an int pair + static const RegMask& secondI_proj_mask(); - // Register for DIVL projection of divmodL - static const RegMask& divL_proj_mask(); - // Register for MODL projection of divmodL - static const RegMask& modL_proj_mask(); + // Register for the first projection of a long pair + static const RegMask& firstL_proj_mask(); + // Register for the second projection of a long pair + static const RegMask& secondL_proj_mask(); // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 4f68ff281a0..165eb9e430c 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -86,12 +86,13 @@ bool MemNode::check_if_adr_maybe_raw(Node* adr) { #ifndef PRODUCT void MemNode::dump_spec(outputStream *st) const { - if (in(Address) == nullptr) return; // node is dead + if (in(Address) == nullptr) { + // node is dead + return; + } #ifndef ASSERT // fake the missing field - const TypePtr* _adr_type = nullptr; - if (in(Address) != nullptr) - _adr_type = in(Address)->bottom_type()->isa_ptr(); + const TypePtr* _adr_type = in(Address)->bottom_type()->isa_ptr(); #endif dump_adr_type(_adr_type, st); @@ -108,6 +109,7 @@ void MemNode::dump_spec(outputStream *st) const { if (_unsafe_access) { st->print(" unsafe"); } + st->print(" barrier(0x%x)", _barrier_data); } void MemNode::dump_adr_type(const TypePtr* adr_type, outputStream* st) { @@ -1288,9 +1290,12 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { return res; } - // Type-unsafe stores must be due to array polymorphism - const TypePtr* adr_type = this->adr_type(); - assert(adr_type == nullptr || adr_type->isa_aryptr() != nullptr, "unexpected type-unsafe store"); + // There are some cases in which the Type of the load is narrower than the Type of the value + // that is stored into that location. The most common case is array polymorphism, when the + // type of an array element depends on the type of the array. In addition, there are some + // corner cases, the first one is concurrent class loading, when CHA can result in a narrower + // Type than what is declared only after the child class is loaded, and the second case is + // unsafe accesses when we do not check for type safety. See JDK-8388184. return nullptr; } @@ -4145,6 +4150,26 @@ MemBarNode* LoadStoreNode::trailing_membar() const { uint LoadStoreNode::size_of() const { return sizeof(*this); } +#ifndef PRODUCT +void LoadStoreNode::dump_spec(outputStream* st) const { + if (in(MemNode::Address) == nullptr) { + // node is dead + return; + } +#ifndef ASSERT + // fake the missing field + const TypePtr* _adr_type = in(MemNode::Address)->bottom_type()->isa_ptr(); +#endif + MemNode::dump_adr_type(_adr_type, st); + + Compile* C = Compile::current(); + if (C->alias_type(_adr_type)->is_volatile()) { + st->print(" Volatile!"); + } + st->print(" barrier(0x%x)", _barrier_data); +} +#endif + //============================================================================= //----------------------------------LoadStoreConditionalNode-------------------- LoadStoreConditionalNode::LoadStoreConditionalNode( Node *c, Node *mem, Node *adr, Node *val, Node *ex ) : LoadStoreNode(c, mem, adr, val, nullptr, TypeInt::BOOL, 5) { diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index f3f65608972..77252520324 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -880,6 +880,10 @@ public: uint8_t barrier_data() { return _barrier_data; } void set_barrier_data(uint8_t barrier_data) { _barrier_data = barrier_data; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif + private: virtual bool depends_only_on_test_impl() const { return false; } }; diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index e48acd23b87..eb24e31eee2 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -26,6 +26,8 @@ #include "opto/addnode.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" +#include "opto/machnode.hpp" +#include "opto/matcher.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" #include "opto/phaseX.hpp" @@ -606,6 +608,36 @@ const Type* UMulHiLNode::Value(PhaseGVN* phase) const { return MulHiValue(t1, t2, bot); } +MulHiLoLNode* MulHiLoLNode::make(Node* mul_hi) { + assert(mul_hi->Opcode() == Op_MulHiL, "expected MulHiL"); + + MulHiLoLNode* mul_hi_lo = new MulHiLoLNode(mul_hi->in(0), mul_hi->in(1), mul_hi->in(2)); + [[maybe_unused]] Node* lo_proj = new ProjNode(mul_hi_lo, MulHiLoLNode::first_proj_num); + [[maybe_unused]] Node* hi_proj = new ProjNode(mul_hi_lo, MulHiLoLNode::second_proj_num); + return mul_hi_lo; +} + +UMulHiLoLNode* UMulHiLoLNode::make(Node* umul_hi) { + assert(umul_hi->Opcode() == Op_UMulHiL, "expected UMulHiL"); + + UMulHiLoLNode* umul_hi_lo = new UMulHiLoLNode(umul_hi->in(0), umul_hi->in(1), umul_hi->in(2)); + [[maybe_unused]] Node* lo_proj = new ProjNode(umul_hi_lo, MulHiLoLNode::first_proj_num); + [[maybe_unused]] Node* hi_proj = new ProjNode(umul_hi_lo, MulHiLoLNode::second_proj_num); + return umul_hi_lo; +} + +Node* MulHiLoLNode::match(const ProjNode* proj, const Matcher* match) { + uint ideal_reg = proj->ideal_reg(); + RegMask rm; + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); + } else { + assert(proj->_con == second_proj_num, "must be lo or hi projection"); + rm.assignFrom(match->secondL_proj_mask()); + } + return new MachProjNode(this, proj->_con, rm, ideal_reg); +} + // A common routine used by UMulHiLNode and MulHiLNode const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { // Either input is TOP ==> the result is TOP diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index 1e19e8ec5cd..f26137dfe49 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.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 @@ -25,6 +25,7 @@ #ifndef SHARE_OPTO_MULNODE_HPP #define SHARE_OPTO_MULNODE_HPP +#include "opto/multnode.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" #include "opto/type.hpp" @@ -32,6 +33,7 @@ // Portions of code courtesy of Clifford Click class PhaseTransform; +class Matcher; //------------------------------MulNode---------------------------------------- // Classic MULTIPLY functionality. This covers all the usual 'multiply' @@ -205,6 +207,31 @@ public: friend const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot); }; +//------------------------------MulHiLoLNode----------------------------------- +// Lower and upper 64-bit results of a signed 64x64->128 multiply. +class MulHiLoLNode : public BinaryMultiNode { +protected: + MulHiLoLNode(Node* ctrl, Node* in1, Node* in2) : BinaryMultiNode(ctrl, in1, in2) {} + +public: + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeTuple::LONG_PAIR; } + + virtual Node* match(const ProjNode* proj, const Matcher* m); + + static MulHiLoLNode* make(Node* mul_hi); +}; + +//------------------------------UMulHiLoLNode---------------------------------- +// Lower and upper 64-bit results of an unsigned 64x64->128 multiply. +class UMulHiLoLNode : public MulHiLoLNode { +public: + UMulHiLoLNode(Node* ctrl, Node* in1, Node* in2) : MulHiLoLNode(ctrl, in1, in2) {} + virtual int Opcode() const; + + static UMulHiLoLNode* make(Node* umul_hi); +}; + //------------------------------AndINode--------------------------------------- // Logically AND 2 integers. Included with the MUL nodes because it inherits // all the behavior of multiplication on a ring. diff --git a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp index b63d418b742..6a69eafb7ed 100644 --- a/src/hotspot/share/opto/multnode.hpp +++ b/src/hotspot/share/opto/multnode.hpp @@ -149,6 +149,34 @@ public: ProjNode* find_first(uint which_proj, bool is_io_use) const; }; +class BinaryMultiNode : public MultiNode { +protected: + BinaryMultiNode(Node* ctrl, Node* in1, Node* in2) : MultiNode(3) { + init_req(0, ctrl); + init_req(1, in1); + init_req(2, in2); + } + +public: + enum { + first_proj_num = 0, + second_proj_num = 1 + }; + + virtual Node* Identity(PhaseGVN* phase) { return this; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } + virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } + virtual uint hash() const { return Node::hash(); } + virtual bool is_CFG() const { return false; } + virtual uint ideal_reg() const { return NotAMachineReg; } + + ProjNode* first_proj() const { return proj_out_or_null(first_proj_num); } + ProjNode* second_proj() const { return proj_out_or_null(second_proj_num); } + +private: + virtual bool depends_only_on_test() const { return false; } +}; + //------------------------------ProjNode--------------------------------------- // This class defines a Projection node. Projections project a single element // out of a tuple (or Signature) type. Only MultiNodes produce TypeTuple diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 997ce92fe1c..726a3ea1b55 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2882,7 +2882,7 @@ bool Node::is_iteratively_computed() { //--------------------------find_similar------------------------------ // Return a node with opcode "opc" and same inputs as "this" if one can // be found; Otherwise return null; -Node* Node::find_similar(int opc) { +Node* Node::find_similar(int opc, bool is_commutative) { if (req() >= 2) { Node* def = in(1); if (def && def->outcnt() >= 2) { @@ -2890,9 +2890,26 @@ Node* Node::find_similar(int opc) { Node* use = def->fast_out(i); if (use != this && use->Opcode() == opc && - use->req() == req() && - has_same_inputs_as(use)) { - return use; + use->req() == req()) { + bool same = false; + if (!is_commutative || req() < 3) { + same = use->has_same_inputs_as(this); + } else { + if (use->in(0) == in(0) && + ((use->in(1) == in(1) && use->in(2) == in(2)) || + (use->in(1) == in(2) && use->in(2) == in(1)))) { + same = true; + for (uint j = 3; j < req(); j++) { + if (use->in(j) != in(j)) { + same = false; + break; + } + } + } + } + if (same) { + return use; + } } } } @@ -3001,6 +3018,27 @@ 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(); } +// Whether this is an intrinsic node that accesses memory and has a memory input, such as array +// equal intrinsic. Some nodes do access memory but do not have a memory input, such as +// PartialSubTypeCheck, they are not included here. +bool Node::is_memory_access_intrinsic() const { + switch (Opcode()) { + case Op_StrComp: + case Op_StrEquals: + case Op_StrIndexOf: + case Op_StrIndexOfChar: + case Op_StrCompressedCopy: + case Op_StrInflatedCopy: + case Op_AryEq: + case Op_CountPositives: + case Op_VectorizedHashCode: + case Op_EncodeISOArray: + return true; + default: + return false; + } +} + //--------------------------has_non_debug_uses------------------------------ // Checks whether the node has any non-debug uses or not. bool Node::has_non_debug_uses() const { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 8c6622e643e..b3de7498e50 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1060,10 +1060,16 @@ public: // The data node which is safe to leave in dead loop during IGVN optimization. bool is_dead_loop_safe() const; + void mark_not_dead_loop_safe() { + assert(is_dead_loop_safe(), "shouldn't be cleared yet"); + remove_flag(Node::Flag_is_dead_loop_safe); + } + // is_Copy() returns copied edge index (0 or 1) uint is_Copy() const { return (_flags & Flag_is_Copy); } virtual bool is_CFG() const { return false; } + bool is_memory_access_intrinsic() const; // If this node is control-dependent on a test, can it be rerouted to a dominating equivalent // test? This means that the node can be executed safely as long as it happens after the test @@ -1310,7 +1316,7 @@ public: // Return a node with opcode "opc" and same inputs as "this" if one can // be found; Otherwise return null; - Node* find_similar(int opc); + Node* find_similar(int opc, bool is_commutative = false); bool has_same_inputs_as(const Node* other) const; // Return the unique control out if only one. Null if none or more than one. diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index 428379e84ae..a3b50d269fb 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -183,6 +183,14 @@ void OpaqueInitializedAssertionPredicateNode::dump_spec(outputStream* st) const } #endif // NOT PRODUCT +// Do NOT remove the opaque node until subsequent IGVN pass. +Node* OpaqueParseNode::Identity(PhaseGVN* phase) { + if (phase->is_IterGVN()) { + return in(1); + } + return this; +} + uint ProfileBooleanNode::hash() const { return NO_HASH; } bool ProfileBooleanNode::cmp( const Node &n ) const { return (&n == this); diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index bb3da2aa65f..7ec7e23144a 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -258,6 +258,19 @@ class OpaqueInitializedAssertionPredicateNode : public Node { NOT_PRODUCT(void dump_spec(outputStream* st) const); }; +// The node is used during late inlining to limit type propagation between cleanup phases. +// It avoids type paradoxes caused by divergence between recorded type and IR shapes +// during successive late inlining attempts. +class OpaqueParseNode : public TypeNode { + public: + OpaqueParseNode(Compile* C, Node* n, const Type* t) : TypeNode(t, 2) { + init_req(1, n); + C->record_for_igvn(this); + } + virtual int Opcode() const; + virtual Node* Identity(PhaseGVN* phase); +}; + //------------------------------ProfileBooleanNode------------------------------- // A node represents value profile for a boolean during parsing. // Once parsing is over, the node goes away (during IGVN). diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 5118019fc31..426720b5bba 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -480,6 +480,7 @@ class Parse : public GraphKit { // Helper: Merge the current mapping into the given basic block void merge_common(Block* target, int pnum); // Helper functions for merging individual cells. + Node* maybe_narrow_phi_input(Node* ctrl, Node* n, const Type* phi_type); PhiNode *ensure_phi( int idx, bool nocreate = false); PhiNode *ensure_memory_phi(int idx, bool nocreate = false); // Helper to merge the current memory state into the given basic block diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 6a400631bff..2d74866e570 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1883,7 +1883,8 @@ void Parse::merge_common(Parse::Block* target, int pnum) { if (phi != nullptr) { assert(n != top() || r->in(pnum) == top(), "live value must not be garbage"); assert(phi->region() == r, ""); - phi->set_req(pnum, n); // Then add 'n' to the merge + phi->set_req(pnum, maybe_narrow_phi_input(r->in(pnum), n, _gvn.type(phi))); + if (pnum == PhiNode::Input) { // Last merge for this Phi. // So far, Phis have had a reasonable type from ciTypeFlow. @@ -2060,6 +2061,21 @@ int Parse::Block::add_new_path() { return pnum; } +// The verifier ensures that the ciType of phi is not narrower than its inputs. However, since +// TypeOopPtr::make_from_klass may be aggressive if it finds that the ciType has only a single +// concrete subtype, and concurrent class loading/unloading may change this property during the +// compilation process, it may be the case that the Type of phi is narrower than its inputs. In +// those cases, we need to insert a CheckCastPP, otherwise several PhiNode idealization may be +// unsound, as we may replace a Phi which has a narrower Type with one of its input which has a +// wider Type. +Node* Parse::maybe_narrow_phi_input(Node* ctrl, Node* n, const Type* phi_type) { + if (phi_type->isa_oopptr() != nullptr && !_gvn.type(n)->higher_equal(phi_type)) { + n = new CheckCastPPNode(ctrl, n, phi_type, ConstraintCastNode::DependencyType::NonFloatingNarrowing); + n = _gvn.transform(n); + } + return n; +} + //------------------------------ensure_phi------------------------------------- // Turn the idx'th entry of the current map into a Phi PhiNode *Parse::ensure_phi(int idx, bool nocreate) { @@ -2108,9 +2124,18 @@ PhiNode *Parse::ensure_phi(int idx, bool nocreate) { return nullptr; } - PhiNode* phi = PhiNode::make(region, o, t); + PhiNode* phi = new PhiNode(region, t); gvn().set_type(phi, t); - if (C->do_escape_analysis()) record_for_igvn(phi); + for (uint i = 1; i < phi->req(); i++) { + Node* ctrl = region->in(i); + if (ctrl != nullptr) { + phi->init_req(i, maybe_narrow_phi_input(ctrl, o, t)); + } + } + + if (C->do_escape_analysis()) { + record_for_igvn(phi); + } map->set_req(idx, phi); return phi; } diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index d732e6f04e1..9cb20cfcd00 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1749,6 +1749,10 @@ static bool match_type_check(PhaseGVN& gvn, Node* con, const Type* tcon, Node* val, const Type* tval, Node** obj, const TypeOopPtr** cast_type) { // out-parameters + assert(tcon->singleton(), "not a constant: %s", Type::str(tcon)); + assert(tcon == gvn.type(con), "mismatch: %s != %s", Type::str(tcon), Type::str(gvn.type(con))); + assert(tval == gvn.type(val), "mismatch: %s != %s", Type::str(tval), Type::str(gvn.type(val))); + // Look for opportunities to sharpen the type of a node whose klass is compared with a constant klass. // The constant klass being tested against can come from many bytecode instructions (implicitly or explicitly), // and also from profile data used by speculative casts. @@ -1783,14 +1787,14 @@ static bool match_type_check(PhaseGVN& gvn, // Region // \ ConI ConI // \ | / - // val -> Phi ConI <- con - // \ / - // CmpI - // | - // Bool [btest] - // | + // val -> Phi ConI|CastII <- con + // \ / + // CmpI + // | + // Bool [btest] + // | // - if (tval->isa_int() && val->is_Phi() && val->in(0)->as_Region()->is_diamond()) { + if (tcon->isa_int() && val->is_Phi() && val->in(0)->as_Region()->is_diamond()) { RegionNode* diamond = val->in(0)->as_Region(); IfNode* if1 = diamond->in(1)->in(0)->as_If(); BoolNode* b1 = if1->in(1)->isa_Bool(); @@ -1799,12 +1803,16 @@ static bool match_type_check(PhaseGVN& gvn, b1->_test._test == BoolTest::ne, "%d", b1->_test._test); ProjNode* success_proj = if1->proj_out(b1->_test._test == BoolTest::eq ? 1 : 0); - int idx = diamond->find_edge(success_proj); - assert(idx == 1 || idx == 2, ""); - Node* vcon = val->in(idx); + int success_idx = diamond->find_edge(success_proj); + assert(success_idx == 1 || success_idx == 2, ""); + assert(val->req() == 3, "not a diamond"); - if ((btest == BoolTest::eq && vcon == con) || (btest == BoolTest::ne && vcon != con)) { - assert(val->find_edge(con) > 0, "mismatch"); + // gen_instanceof() emits 1 on success and 0 on failure. + // Check whether current comparison selects the success value. + const Type* success_tval = gvn.type(val->in(success_idx)); + assert(success_tval->isa_int(), "not an int: %s", Type::str(success_tval)); + if ((btest == BoolTest::eq && tcon == success_tval) || + (btest == BoolTest::ne && tcon->join(success_tval)->empty())) { SubTypeCheckNode* sub = b1->in(1)->as_SubTypeCheck(); Node* obj_or_subklass = sub->in(SubTypeCheckNode::ObjOrSubKlass); Node* superklass = sub->in(SubTypeCheckNode::SuperKlass); @@ -2653,17 +2661,17 @@ void Parse::do_one_bytecode() { case Bytecodes::_i2b: // Sign extend a = pop(); - a = Compile::narrow_value(T_BYTE, a, nullptr, &_gvn, true); + a = Compile::narrow_value(T_BYTE, a, TypeInt::BYTE, &_gvn, true); push(a); break; case Bytecodes::_i2s: a = pop(); - a = Compile::narrow_value(T_SHORT, a, nullptr, &_gvn, true); + a = Compile::narrow_value(T_SHORT, a, TypeInt::SHORT, &_gvn, true); push(a); break; case Bytecodes::_i2c: a = pop(); - a = Compile::narrow_value(T_CHAR, a, nullptr, &_gvn, true); + a = Compile::narrow_value(T_CHAR, a, TypeInt::CHAR, &_gvn, true); push(a); break; diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 7c0f12f6ef7..e5e34051587 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -222,12 +222,16 @@ public: return TypeIntHelper::int_type_union(this, &o); } + bool contains(U u) const { + S s = S(u); + return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); + } + // These allow TypeIntMirror to mimick the behaviors of TypeInt* and TypeLong*, so they can be // passed into RangeInference methods. These are only used in testing, so they are implemented in // the test file. static TypeIntMirror make(const TypeIntMirror& t, int widen); const TypeIntMirror* operator->() const; - bool contains(U u) const; bool contains(const TypeIntMirror& o) const; bool operator==(const TypeIntMirror& o) const; @@ -365,20 +369,23 @@ private: return CT::make(res, MAX2(t1->_widen, t2->_widen)); } + template + static TypeIntMirror, U> infer_and_impl(const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { + S lo = std::numeric_limits>::min(); + S hi = std::numeric_limits>::max(); + U ulo = std::numeric_limits>::min(); + // The unsigned value of the result of 'and' is always not greater than both of its inputs + // since there is no position at which the bit is 1 in the result and 0 in either input + U uhi = MIN2(st1._uhi, st2._uhi); + U zeros = st1._bits._zeros | st2._bits._zeros; + U ones = st1._bits._ones & st2._bits._ones; + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); + } + public: template static CTP infer_and(CTP t1, CTP t2) { - return infer_binary(t1, t2, [&](const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { - S lo = std::numeric_limits>::min(); - S hi = std::numeric_limits>::max(); - U ulo = std::numeric_limits>::min(); - // The unsigned value of the result of 'and' is always not greater than both of its inputs - // since there is no position at which the bit is 1 in the result and 0 in either input - U uhi = MIN2(st1._uhi, st2._uhi); - U zeros = st1._bits._zeros | st2._bits._zeros; - U ones = st1._bits._ones & st2._bits._ones; - return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); - }); + return infer_binary(t1, t2, infer_and_impl); } template @@ -442,6 +449,104 @@ public: TypeIntPrototype, U> proto{{slo, shi}, {ulo, uhi}, known_bits}; return CT::make(proto, t1->_widen); } + + // Bit compression selects the source bits corresponding to true mask bits, packs them and places + // them contiguously at destination bit positions starting from least significant bit, remaining + // higher order bits are set to zero. + template + static CTP infer_compress_bits(CTP t1, CTP t2) { + return infer_binary(t1, t2, [](const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { + S lo = std::numeric_limits>::min(); + const S hi = std::numeric_limits>::max(); + const U ulo = U(0); + // Integer.compress(v, mask) == Integer.compress(v & mask, mask) + // Integer.compress(v, mask) u<= v + // So, Integer.compress(v, mask) u<= (v & mask) + const U uhi = infer_and_impl(st1, st2)._uhi; + // If the mask has at least 1 unset bit, then the result must have its highest bit unset, and + // since the only value with no unset bit is the maximum unsigned value, if st2 does not + // contain that value, the result must be non-negative + if (!st2.contains(std::numeric_limits>::max())) { + lo = S(0); + } + + U zeros = U(0); + U ones = U(0); + // Firstly, try to collect known bits by traversing from the lowest to the highest bits, we + // can collect bits up to the first position at which the corresponding bit in the second + // operand is unknown. + // For example, consider Integer.compress(v, mask), with: + // v = 0bxyztuv + // mask = 0b*1*110 + // we can walk the lowest 3 bits of the operands, and determine that the result must be + // 0b****tu + { + // The bit index in result that will be taken from the current bit in the first operand, + // can only be known if we have not encountered any unknown bit in the second operand + int res_bit_idx = 0; + for (int op_bit_idx = 0; op_bit_idx < HotSpotNumerics::type_width>(); op_bit_idx++) { + // If the bit is 0 in the second operand, the corresponding bit value in the first + // operand is irrelevant + U op_bit_mask = U(1) << op_bit_idx; + if ((st2._bits._zeros & op_bit_mask) != U(0)) { + continue; + } + + // No further analysis is possible + if ((st2._bits._ones & op_bit_mask) == U(0)) { + break; + } + + // The bit of the second operand at op_bit_idx must be 1 + U res_bit_mask = U(1) << res_bit_idx; + if ((st1._bits._zeros & op_bit_mask) != U(0)) { + zeros |= res_bit_mask; + } else if ((st1._bits._ones & op_bit_mask) != U(0)) { + ones |= res_bit_mask; + } + res_bit_idx++; + } + } + + // Secondly, try to infer the number of leading zeros by traversing from the highest to the + // lowest bits. Integer.compress(v, mask) == Integer.compress(v & mask, mask), so the number + // of leading zeros in the result is not less than the number of leading zeros in (v & mask). + // Furthermore, in the remaining bits, for each bit in the second operand that must be 0, an + // addition leading zero in result is guaranteed. + // For example, consider Integer.compress(v, mask), with: + // v = 0b*01*** + // mask = 0b0x1*0* + // v & mask = 0b001*0* + // So the result must have at least 2 leading zeros. Furthermore, we can see that it is + // irrelevant whether the bit x in mask is 0 or 1, because the bit in result corresponding to + // x must be 0, and the result must have no higher set bit in either case. As a result, we + // can assume mask = 0b001*0*. And since mask has at least 3 unset bits, the result must have + // at least 3 leading zeros. + { + // The bit index in result that is determined to be 0 + int res_bit_idx = HotSpotNumerics::type_width>() - 1; + // Whether we have encountered a bit that is not known 0 in either the first or the second + // operand + bool leading_zeros = true; + for (int op_bit_idx = HotSpotNumerics::type_width>() - 1; op_bit_idx >= 0; op_bit_idx--) { + U op_bit_mask = U(1) << op_bit_idx; + if ((st2._bits._zeros & op_bit_mask) != U(0)) { + zeros |= (U(1) << res_bit_idx); + res_bit_idx--; + } else if (leading_zeros) { + if ((st1._bits._zeros & op_bit_mask) != U(0)) { + zeros |= (U(1) << res_bit_idx); + res_bit_idx--; + } else { + leading_zeros = false; + } + } + } + } + + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); + }); + } }; #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index f9fa02317bc..8e0f6f5bf43 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -29,6 +29,7 @@ #include "opto/phaseX.hpp" #include "opto/rootnode.hpp" #include "opto/vector.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" static bool is_vector_mask(ciKlass* klass) { @@ -455,11 +456,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); + vec_field_ld = bs->load_at(access, Type::get_const_basic_type(T_OBJECT)); // 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); + vec_field_ld = gvn.transform(new CheckCastPPNode(ctrl, vec_field_ld, payload_type, ConstraintCastNode::DependencyType::NonFloatingNarrowing)); } Node* adr = kit.array_element_address(vec_field_ld, gvn.intcon(0), bt); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index f5162c4bb2f..d04eda60b81 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -174,7 +174,10 @@ Node* GraphKit::unbox_vector(Node* v, const TypeInstPtr* vbox_type, BasicType el } assert(check_vbox(vbox_type), ""); const TypeVect* vt = TypeVect::make(elem_bt, num_elem, is_vector_mask(vbox_type->instance_klass())); - Node* unbox = gvn().transform(new VectorUnboxNode(C, vt, v, merged_memory())); + Node* ctrl = control(); + Node* mem = reset_memory(); + set_all_memory(mem); + Node* unbox = gvn().transform(new VectorUnboxNode(C, vt, ctrl, v, mem)); if (gvn().type(unbox)->isa_vect() == nullptr) { assert(gvn().type(unbox) == Type::TOP, "sanity"); return nullptr; // not a vector @@ -298,7 +301,7 @@ static bool is_klass_initialized(const TypeInstPtr* vec_klass) { } static bool is_primitive_lane_type(VectorSupport::LaneType laneType) { - return laneType >= VectorSupport::LT_FLOAT && laneType <= VectorSupport::LT_LONG; + return laneType >= VectorSupport::LT_FLOAT && laneType <= VectorSupport::LT_FLOAT16; } static BasicType get_vector_primitive_lane_type(VectorSupport::LaneType lanetype) { @@ -310,10 +313,15 @@ static BasicType get_vector_primitive_lane_type(VectorSupport::LaneType lanetype case VectorSupport::LaneType::LT_INT: return T_INT; case VectorSupport::LaneType::LT_SHORT: return T_SHORT; case VectorSupport::LaneType::LT_BYTE: return T_BYTE; + case VectorSupport::LaneType::LT_FLOAT16: return T_SHORT; } return T_ILLEGAL; } +static bool is_supported_lane_type(VectorSupport::LaneType laneType) { + return laneType >= VectorSupport::LT_FLOAT && laneType <= VectorSupport::LT_LONG; +} + // // , // M extends VectorMask, @@ -557,6 +565,11 @@ bool LibraryCallKit::inline_vector_call(int arity) { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); @@ -651,6 +664,11 @@ bool LibraryCallKit::inline_vector_mask_operation() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + int num_elem = vlen->get_con(); BasicType elem_bt = get_vector_primitive_lane_type(vltype); int mopc = VectorSupport::vop2ideal(oper->get_con(), vltype); @@ -721,6 +739,12 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { return false; } + int bcast_mode = mode->get_con(); + if (!is_supported_lane_type(vltype) && bcast_mode != VectorSupport::MODE_BROADCAST) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; // should be primitive type + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -732,7 +756,6 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); bool is_mask = is_vector_mask(vbox_klass); - int bcast_mode = mode->get_con(); VectorMaskUseType checkFlags = (VectorMaskUseType)(is_mask ? VecMaskUseAll : VecMaskNotUsed); int opc = bcast_mode == VectorSupport::MODE_BITS_COERCED_LONG_TO_MASK ? Op_VectorLongToMask : Op_Replicate; @@ -1296,6 +1319,11 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + BasicType elem_bt = get_vector_primitive_lane_type(vltype); int num_elem = vlen->get_con(); int idx_num_elem = idx_vlen->get_con(); @@ -1479,6 +1507,10 @@ bool LibraryCallKit::inline_vector_reduction() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } BasicType elem_bt = get_vector_primitive_lane_type(vltype); const Type* vmask_type = gvn().type(argument(6)); bool is_masked_op = vmask_type != TypePtr::NULL_PTR; @@ -1624,6 +1656,11 @@ bool LibraryCallKit::inline_vector_test() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -1773,6 +1810,11 @@ bool LibraryCallKit::inline_vector_compare() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(mask_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -1893,6 +1935,10 @@ bool LibraryCallKit::inline_vector_rearrange() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } BasicType elem_bt = get_vector_primitive_lane_type(vltype); BasicType shuffle_bt = elem_bt; if (shuffle_bt == T_FLOAT) { @@ -2029,6 +2075,10 @@ bool LibraryCallKit::inline_vector_select_from() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } int num_elem = vlen->get_con(); BasicType elem_bt = get_vector_primitive_lane_type(vltype); if (!is_power_of_2(num_elem)) { @@ -2193,6 +2243,11 @@ bool LibraryCallKit::inline_vector_broadcast_int() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + const Type* vmask_type = gvn().type(argument(7)); bool is_masked_op = vmask_type != TypePtr::NULL_PTR; if (is_masked_op) { @@ -2369,6 +2424,16 @@ bool LibraryCallKit::inline_vector_convert() { log_if_needed(" ** not a primitive to lt=%s", VectorSupport::lanetype2name(vltype_to)); return false; // should be primitive type } + + if (!is_supported_lane_type(vltype_from)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype_from)); + return false; + } + + if (!is_supported_lane_type(vltype_to)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype_to)); + return false; + } BasicType elem_bt_from = get_vector_primitive_lane_type(vltype_from); BasicType elem_bt_to = get_vector_primitive_lane_type(vltype_to); @@ -2550,6 +2615,11 @@ bool LibraryCallKit::inline_vector_insert() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -2638,6 +2708,11 @@ bool LibraryCallKit::inline_vector_extract() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -2822,6 +2897,11 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -2960,6 +3040,11 @@ bool LibraryCallKit::inline_vector_compress_expand() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + int num_elem = vlen->get_con(); BasicType elem_bt = get_vector_primitive_lane_type(vltype); int opc = VectorSupport::vop2ideal(opr->get_con(), vltype); @@ -3035,6 +3120,11 @@ bool LibraryCallKit::inline_index_vector() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -3170,6 +3260,11 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() { return false; } + if (!is_supported_lane_type(vltype)) { + log_if_needed(" ** unsupported lane type =%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(mask_klass)) { log_if_needed(" ** klass argument not initialized"); return false; diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index dd49d88ce96..20857eed35c 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -874,6 +874,7 @@ VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, Node* n3, const TypeV case Op_SignumVD: return new SignumVDNode(n1, n2, n3, vt); case Op_SignumVF: return new SignumVFNode(n1, n2, n3, vt); case Op_VectorBlend: return new VectorBlendNode(n1, n2, n3); + case Op_VectorBitwiseBlend: return new VectorBitwiseBlendNode(n1, n2, n3, vt); default: fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); return nullptr; @@ -1343,6 +1344,14 @@ Node* VectorNode::reassociate_vector_operation(PhaseGVN* phase) { return nullptr; } + // Reassociation is beneficial if transformed node with replicate inputs can + // subsequently be collapsed by push_through_replicate into Replicate(ScalarOp(..)). + // That folding needs a scalar opcode for this operation/element type. + // Safety check to ensure we skip useless/redundant reassociations. + if (scalar_opcode(Opcode(), vect_type()->element_basic_type()) == 0) { + return nullptr; + } + Node* in1 = in(1); Node* in2 = in(2); if (in2->Opcode() == Op_Replicate && in1->Opcode() == Opcode()) { @@ -1407,6 +1416,13 @@ Node* VectorNode::push_through_replicate(PhaseGVN* phase) { sop = phase->transform(sop); + // For subword types, the scalar operation computes at int width and may + // produce values outside the subword range. Narrow the result unconditionally + // before feeding it to Replicate. + if (is_subword_type(bt)) { + sop = Compile::narrow_value(bt, sop, Type::get_const_basic_type(bt), phase, true); + } + return new ReplicateNode(sop, vect_type()); } @@ -2293,7 +2309,13 @@ Node* VectorUnboxNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (is_vector_mask) { // VectorUnbox (VectorBox vmask) ==> VectorMaskCast vmask const TypeVect* vmask_type = TypeVect::makemask(out_vt->element_basic_type(), out_vt->length()); - return new VectorMaskCastNode(value, vmask_type); + const TypeVect* value_type = value->bottom_type()->is_vect(); + // Very rarely, profiling can give us output types that are not + // compatible with the input type, where one is PVectMask and + // the other not. Such a path should be unreachable anyway. + if ((value_type->isa_pvectmask() == nullptr) == (vmask_type->isa_pvectmask() == nullptr)) { + return new VectorMaskCastNode(value, vmask_type); + } } else { // Vector type mismatch is only supported for masks, but sometimes it happens in pathological cases. } @@ -2768,6 +2790,70 @@ Node* XorVNode::Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape) { return res; } +// XorV(a, AndV(sel, XorV(a, b))) => VectorBitwiseBlend(a, b, sel) +// XorV(a, AndV(sel, XorV(a, b)), mask) => +// VectorBlend(a, VectorBitwiseBlend(a, b, sel), mask) +Node* XorVNode::Ideal_XorV_to_VectorBitwiseBlend(PhaseGVN* phase, bool can_reshape) { + const TypeVect* vt = vect_type(); + BasicType bt = vt->element_basic_type(); + uint vlen = vt->length(); + if (!Matcher::match_rule_supported_vector(Op_VectorBitwiseBlend, vlen, bt)) { + return nullptr; + } + + bool is_masked = is_predicated_vector(); + if (is_masked && + !Matcher::match_rule_supported_vector(Op_VectorBlend, vlen, bt)) { + return nullptr; + } + + // For the predicated case in(1) is fixed as the merge source. Otherwise the + // outer XorV is commutative. + Node* a = nullptr; + Node* andv = nullptr; + if (is_masked || in(2)->Opcode() == Op_AndV) { + andv = in(2); + a = in(1); + } else { + andv = in(1); + a = in(2); + } + if (andv->Opcode() != Op_AndV || andv->is_predicated_vector()) { + return nullptr; + } + + Node* sel = nullptr; + Node* inner_xor = nullptr; + if (andv->in(2)->Opcode() == Op_XorV) { + inner_xor = andv->in(2); + sel = andv->in(1); + } else if (andv->in(1)->Opcode() == Op_XorV) { + inner_xor = andv->in(1); + sel = andv->in(2); + } else { + return nullptr; + } + if (inner_xor->is_predicated_vector()) { + return nullptr; + } + + Node* b = nullptr; + if (inner_xor->in(1) == a) { + b = inner_xor->in(2); + } else if (inner_xor->in(2) == a) { + b = inner_xor->in(1); + } else { + return nullptr; + } + + Node* blend = new VectorBitwiseBlendNode(a, b, sel, vt); + if (!is_masked) { + return blend; + } + blend = phase->transform(blend); + return new VectorBlendNode(a, blend, in(3)); +} + Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) { // (XorV src src) => (Replicate zero) // (XorVMask src src) => (MaskAll zero) @@ -2786,6 +2872,11 @@ Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (res != nullptr) { return res; } + + res = Ideal_XorV_to_VectorBitwiseBlend(phase, can_reshape); + if (res != nullptr) { + return res; + } return VectorNode::Ideal(phase, can_reshape); } @@ -2800,18 +2891,20 @@ static bool is_replicate_uint_constant(const Node* n) { return n->Opcode() == Op_Replicate && n->in(1)->is_Con() && n->in(1)->bottom_type()->isa_long() && - n->in(1)->bottom_type()->is_long()->get_con() <= 0xFFFFFFFFL; + (julong)n->in(1)->bottom_type()->is_long()->get_con() <= 0xFFFFFFFFUL; } static bool has_vector_elements_fit_uint(Node* n) { auto is_lower_doubleword_mask_pattern = [](const Node* n) { return n->Opcode() == Op_AndV && + !n->is_predicated_vector() && (is_replicate_uint_constant(n->in(1)) || is_replicate_uint_constant(n->in(2))); }; auto is_clear_upper_doubleword_uright_shift_pattern = [](const Node* n) { return n->Opcode() == Op_URShiftVL && + !n->is_predicated_vector() && n->in(2)->Opcode() == Op_RShiftCntV && n->in(2)->in(1)->is_Con() && n->in(2)->in(1)->bottom_type()->isa_int() && n->in(2)->in(1)->bottom_type()->is_int()->get_con() >= 32; @@ -2827,6 +2920,7 @@ static bool has_vector_elements_fit_int(Node* n) { auto is_clear_upper_doubleword_right_shift_pattern = [](const Node* n) { return n->Opcode() == Op_RShiftVL && + !n->is_predicated_vector() && n->in(2)->Opcode() == Op_RShiftCntV && n->in(2)->in(1)->is_Con() && n->in(2)->in(1)->bottom_type()->isa_int() && n->in(2)->in(1)->bottom_type()->is_int()->get_con() >= 32; diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index de077015bca..d013bbc25d6 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1075,6 +1075,7 @@ class XorVNode : public VectorNode { virtual int Opcode() const; virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); Node* Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape); + Node* Ideal_XorV_to_VectorBitwiseBlend(PhaseGVN* phase, bool can_reshape); }; // Vector xor byte, short, int, long as a reduction @@ -1802,6 +1803,24 @@ class VectorBlendNode : public VectorNode { Node* vec_mask() const { return in(3); } }; +// Vector bitwise blend (bit-select): (sel & vec_true) | (~sel & vec_false). +class VectorBitwiseBlendNode : public VectorNode { + public: + VectorBitwiseBlendNode(Node* vec_false, Node* vec_true, Node* sel, const TypeVect* vt) + : VectorNode(vec_false, vec_true, sel, vt) { + assert(vec_false->bottom_type()->isa_vect() != nullptr && + vec_true->bottom_type()->isa_vect() != nullptr && + sel->bottom_type()->isa_vect() != nullptr, + "inputs must all be vectors"); + uint vlen = vt->length(); + assert(vec_false->bottom_type()->is_vect()->length() == vlen && + vec_true->bottom_type()->is_vect()->length() == vlen && + sel->bottom_type()->is_vect()->length() == vlen, + "mismatched vector length"); + } + virtual int Opcode() const; +}; + // Rearrange lane elements from a source vector under the control of a shuffle // (indexes) vector. Each lane in the shuffle vector specifies which lane from // the source vector to select for the corresponding output lane. All indexes @@ -2137,11 +2156,10 @@ class VectorBoxAllocateNode : public CallStaticJavaNode { // vector value. This is a macro node expanded during vector optimization // phase. class VectorUnboxNode : public VectorNode { - protected: - uint size_of() const { return sizeof(*this); } - public: - VectorUnboxNode(Compile* C, const TypeVect* vec_type, Node* obj, Node* mem) +public: + VectorUnboxNode(Compile* C, const TypeVect* vec_type, Node* ctrl, Node* obj, Node* mem) : VectorNode(mem, obj, vec_type) { + init_req(0, ctrl); init_class_id(Class_VectorUnbox); init_flags(Flag_is_macro); C->add_macro_node(this); @@ -2152,6 +2170,10 @@ class VectorUnboxNode : public VectorNode { Node* mem() const { return in(1); } virtual Node* Identity(PhaseGVN* phase); Node* Ideal(PhaseGVN* phase, bool can_reshape); + +private: + uint size_of() const { return sizeof(*this); } + bool depends_only_on_test_impl() const { return false; } }; // Lane-wise right rotation of the first input by the second input. diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 9fff32f8e78..4c9e3d67d5f 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1371,16 +1371,10 @@ JvmtiEnvBase::set_frame_pop(JvmtiThreadState* state, javaVFrame* jvf, jint depth return JVMTI_ERROR_OPAQUE_FRAME; } - if (state->is_virtual() && (thread == nullptr || !thread->is_vthread_mounted())) { // unmounted virtual thread - assert(fr.is_heap_frame(), "sanity check"); - fr = jvf->stack_chunk()->derelativize(fr); - jvf->stack_chunk()->force_slow_path(); - fr.deoptimize(nullptr); - } else { // platform thread or mounted virtual thread - if (fr.is_heap_frame()) { - fr = jvf->stack_chunk()->derelativize(fr); - jvf->stack_chunk()->force_slow_path(); - } + if (fr.is_heap_frame()) { + assert(state->is_virtual(), "invariant"); + fr.deoptimize(nullptr, jvf->stack_chunk()); + } else { Deoptimization::deoptimize(thread, fr); } } @@ -2318,6 +2312,7 @@ SetForceEarlyReturn::doit(Thread *target) { // Set pending step flag for this early return. // It is cleared when next step event is posted. _state->set_pending_step_for_earlyret(); + _state->invalidate_cur_stack_depth(); } void diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index c0a4ca949c9..96366cceaff 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -379,7 +379,7 @@ bool VM_BaseGetOrSetLocal::check_slot_type_lvt(javaVFrame* jvf) { if (!method->has_localvariable_table()) { // Just to check index boundaries. jint extra_slot = (_type == T_LONG || _type == T_DOUBLE) ? 1 : 0; - if (_index < 0 || _index + extra_slot >= method->max_locals()) { + if (_index < 0 || _index >= method->max_locals() - extra_slot) { _result = JVMTI_ERROR_INVALID_SLOT; return false; } @@ -451,7 +451,7 @@ bool VM_BaseGetOrSetLocal::check_slot_type_no_lvt(javaVFrame* jvf) { Method* method = jvf->method(); jint extra_slot = (_type == T_LONG || _type == T_DOUBLE) ? 1 : 0; - if (_index < 0 || _index + extra_slot >= method->max_locals()) { + if (_index < 0 || _index >= method->max_locals() - extra_slot) { _result = JVMTI_ERROR_INVALID_SLOT; return false; } diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index 5f496bbc6d4..a1bcf3a0137 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -409,7 +409,8 @@ int JvmtiThreadState::cur_stack_depth() { guarantee(get_thread()->is_handshake_safe_for(current), "must be current thread or direct handshake"); - if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) { + if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH + || is_pending_step_for_earlyret() || is_pending_step_for_popframe()) { _cur_stack_depth = count_frames(); } else { #ifdef ASSERT @@ -474,7 +475,7 @@ void JvmtiThreadState::process_pending_step_for_popframe() { void JvmtiThreadState::update_for_pop_top_frame() { // remove any frame pop notification request for the top frame // in any environment - int popframe_number = cur_stack_depth(); + int popframe_number = count_frames(); { JvmtiEnvThreadStateIterator it(this); for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) { diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index b7f5b427eda..722164ff169 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -190,21 +190,12 @@ class MemoryAccess : StackObj { } template - U normalize_for_write(U x) { + U normalize(U x) { return x; } - jboolean normalize_for_write(jboolean x) { - return x & 1; - } - - template - U normalize_for_read(U x) { - return x; - } - - jboolean normalize_for_read(jboolean x) { - return x != 0; + jboolean normalize(jboolean x) { + return (x & 1) != 0; } public: @@ -215,7 +206,7 @@ public: T get() { GuardUnsafeAccess guard(_thread); - return normalize_for_read(*addr()); + return normalize(*addr()); } // we use this method at some places for writing to 0 e.g. to cause a crash; @@ -223,19 +214,19 @@ public: ATTRIBUTE_NO_UBSAN void put(T x) { GuardUnsafeAccess guard(_thread); - *addr() = normalize_for_write(x); + *addr() = normalize(x); } T get_volatile() { GuardUnsafeAccess guard(_thread); volatile T ret = RawAccess::load(addr()); - return normalize_for_read(ret); + return normalize(ret); } void put_volatile(T x) { GuardUnsafeAccess guard(_thread); - RawAccess::store(addr(), normalize_for_write(x)); + RawAccess::store(addr(), normalize(x)); } }; diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index 5c6010acdf1..badfb8e9b82 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -206,9 +206,10 @@ const char* VectorSupport::lanetype2name(LaneType lane_type) { "byte", "short", "int", - "long" + "long", + "float16", }; - if (lane_type >= LT_FLOAT && lane_type <= LT_LONG) { + if (lane_type >= LT_FLOAT && lane_type <= LT_FLOAT16) { return lanetype2name[lane_type]; } assert(false, "unknown lane type: %d", (int)lane_type); @@ -224,6 +225,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_AddI; case LT_LONG: return Op_AddL; + case LT_FLOAT16: return Op_AddHF; case LT_FLOAT: return Op_AddF; case LT_DOUBLE: return Op_AddD; default: return 0; @@ -236,6 +238,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_SubI; case LT_LONG: return Op_SubL; + case LT_FLOAT16: return Op_SubHF; case LT_FLOAT: return Op_SubF; case LT_DOUBLE: return Op_SubD; default: return 0; @@ -248,6 +251,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_MulI; case LT_LONG: return Op_MulL; + case LT_FLOAT16: return Op_MulHF; case LT_FLOAT: return Op_MulF; case LT_DOUBLE: return Op_MulD; default: return 0; @@ -260,6 +264,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_DivI; case LT_LONG: return Op_DivL; + case LT_FLOAT16: return Op_DivHF; case LT_FLOAT: return Op_DivF; case LT_DOUBLE: return Op_DivD; default: return 0; @@ -272,6 +277,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: return Op_MinI; case LT_LONG: return Op_MinL; + case LT_FLOAT16: return Op_MinHF; case LT_FLOAT: return Op_MinF; case LT_DOUBLE: return Op_MinD; default: return 0; @@ -284,6 +290,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: return Op_MaxI; case LT_LONG: return Op_MaxL; + case LT_FLOAT16: return Op_MaxHF; case LT_FLOAT: return Op_MaxF; case LT_DOUBLE: return Op_MaxD; default: return 0; @@ -316,6 +323,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_AbsI; case LT_LONG: return Op_AbsL; + case LT_FLOAT16: return 0; case LT_FLOAT: return Op_AbsF; case LT_DOUBLE: return Op_AbsD; default: return 0; @@ -328,6 +336,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_NegI; case LT_LONG: return Op_NegL; + case LT_FLOAT16: return 0; case LT_FLOAT: return Op_NegF; case LT_DOUBLE: return Op_NegD; default: return 0; @@ -366,6 +375,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { } case VECTOR_OP_SQRT: { switch (lt) { + case LT_FLOAT16: return Op_SqrtHF; case LT_FLOAT: return Op_SqrtF; case LT_DOUBLE: return Op_SqrtD; default: return 0; @@ -374,6 +384,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { } case VECTOR_OP_FMA: { switch (lt) { + case LT_FLOAT16: return Op_FmaHF; case LT_FLOAT: return Op_FmaF; case LT_DOUBLE: return Op_FmaD; default: return 0; @@ -436,6 +447,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskLastTrue; default: return 0; @@ -448,6 +460,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskFirstTrue; default: return 0; @@ -460,6 +473,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskTrueCount; default: return 0; @@ -472,6 +486,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskToLong; default: return 0; @@ -484,6 +499,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_ExpandV; default: return 0; @@ -496,6 +512,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_CompressV; default: return 0; @@ -508,6 +525,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: // fall-through + case LT_FLOAT16: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_CompressM; default: return 0; diff --git a/src/hotspot/share/prims/vectorSupport.hpp b/src/hotspot/share/prims/vectorSupport.hpp index da3736b4711..f33c56a9b84 100644 --- a/src/hotspot/share/prims/vectorSupport.hpp +++ b/src/hotspot/share/prims/vectorSupport.hpp @@ -144,7 +144,8 @@ class VectorSupport : AllStatic { LT_BYTE = 2, LT_SHORT = 3, LT_INT = 4, - LT_LONG = 5 + LT_LONG = 5, + LT_FLOAT16 = 6 }; enum { diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 2804224ed01..b08e71f559a 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -529,6 +529,7 @@ 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() }, + { "CompilationMode", JDK_Version::jdk(28), JDK_Version::jdk(29), JDK_Version::jdk(30)}, // --- 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() }, { "InitiatingHeapOccupancyPercent", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) }, @@ -1702,6 +1703,7 @@ jint Arguments::parse_vm_init_args(GrowableArrayCHeapset_stack_chunk(nullptr); -#if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) && !defined(ZERO) +#if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) && !defined(ZERO) frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC()); #else frame sender = frame(); diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index d76652edf36..e9b6325d03b 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -220,7 +220,6 @@ template static inline freeze_result freeze_inte static inline int prepare_thaw_internal(JavaThread* thread, bool return_barrier); template static inline intptr_t* thaw_internal(JavaThread* thread, const Continuation::thaw_kind kind); - // Entry point to freeze. Transitions are handled manually // Called from gen_continuation_yield() in sharedRuntime_.cpp through Continuation::freeze_entry(); template @@ -507,13 +506,7 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* assert(!Interpreter::contains(_cont.entryPC()), ""); - _bottom_address = _cont.entrySP() - _cont.entry_frame_extension(); -#ifdef _LP64 - if (((intptr_t)_bottom_address & 0xf) != 0) { - _bottom_address--; - } - assert(is_aligned(_bottom_address, frame::frame_alignment), ""); -#endif + _bottom_address = align_down(_cont.entrySP() - _cont.entry_frame_extension(), frame::frame_alignment); log_develop_trace(continuations)("bottom_address: " INTPTR_FORMAT " entrySP: " INTPTR_FORMAT " argsize: " PTR_FORMAT, p2i(_bottom_address), p2i(_cont.entrySP()), (_cont.entrySP() - _bottom_address) << LogBytesPerWord); @@ -523,13 +516,17 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* assert(_cont.chunk_invariant(), ""); assert(!Interpreter::contains(_cont.entryPC()), ""); -#if !defined(PPC64) || defined(ZERO) - static const int doYield_stub_frame_size = frame::metadata_words; -#else +#if defined(PPC64) && !defined(ZERO) static const int doYield_stub_frame_size = frame::native_abi_reg_args_size >> LogBytesPerWord; +#elif defined(S390) && !defined(ZERO) + static const int doYield_stub_frame_size = frame::z_abi_160_base_size >> LogBytesPerWord; +#else + static const int doYield_stub_frame_size = frame::metadata_words; #endif // With preemption doYield() might not have been resolved yet - assert(_preempt || SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, ""); + assert(_preempt || SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, + "_preempt = %d, cont_doYield_stub()->frame_size() = %d, doYield_stub_frame_size = %d", + (_preempt ? 1 : 0), SharedRuntime::cont_doYield_stub()->frame_size(), doYield_stub_frame_size); if (preempt) { _last_frame = _thread->last_frame(); @@ -2597,7 +2594,13 @@ inline void ThawBase::patch(frame& f, const frame& caller, bool bottom) { } else if (_should_patch_caller_pc) { // Caller was deoptimized during thaw but we've overwritten the return address when copying f from the heap. // Also, on some platforms, if the caller is interpreted but the callee not we also need to patch. - assert(caller.is_deoptimized_frame() PPC64_ONLY(|| caller.is_interpreted_frame()), ""); + +#if defined(PPC64) || defined(S390) + assert(caller.is_deoptimized_frame() || caller.is_interpreted_frame(), ""); +#else + assert(caller.is_deoptimized_frame(), ""); +#endif + ContinuationHelper::Frame::patch_pc(caller, caller.raw_pc()); _should_patch_caller_pc = false; } diff --git a/src/hotspot/share/runtime/cpuTimeCounters.cpp b/src/hotspot/share/runtime/cpuTimeCounters.cpp index e174407089c..3374a1c5db3 100644 --- a/src/hotspot/share/runtime/cpuTimeCounters.cpp +++ b/src/hotspot/share/runtime/cpuTimeCounters.cpp @@ -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. * Copyright (c) 2023 Google LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,6 +25,7 @@ #include "runtime/atomicAccess.hpp" #include "runtime/cpuTimeCounters.hpp" +#include "utilities/globalCounter.inline.hpp" const char* CPUTimeGroups::to_string(CPUTimeType val) { switch (val) { @@ -77,6 +78,10 @@ void CPUTimeCounters::inc_gc_total_cpu_time(jlong diff) { } void CPUTimeCounters::publish_gc_total_cpu_time() { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData()) { + return; + } CPUTimeCounters* instance = CPUTimeCounters::get_instance(); // Atomically fetch the current _gc_total_cpu_time_diff and reset it to zero. jlong new_value = 0; @@ -103,6 +108,10 @@ PerfCounter* CPUTimeCounters::get_counter(CPUTimeGroups::CPUTimeType name) { } void CPUTimeCounters::update_counter(CPUTimeGroups::CPUTimeType name, jlong total) { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData()) { + return; + } CPUTimeCounters* instance = CPUTimeCounters::get_instance(); PerfCounter* counter = instance->get_counter(name); jlong prev_value = counter->get_value(); diff --git a/src/hotspot/share/runtime/cpuTimeCounters.hpp b/src/hotspot/share/runtime/cpuTimeCounters.hpp index c2e636bdb1d..15f680c06e1 100644 --- a/src/hotspot/share/runtime/cpuTimeCounters.hpp +++ b/src/hotspot/share/runtime/cpuTimeCounters.hpp @@ -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. * Copyright (c) 2023 Google LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -79,6 +79,8 @@ private: static void inc_gc_total_cpu_time(jlong diff); + static PerfCounter* get_counter(CPUTimeGroups::CPUTimeType name); + public: static void initialize() { assert(_instance == nullptr, "we can only allocate one CPUTimeCounters object"); @@ -91,7 +93,6 @@ public: } static void create_counter(CPUTimeGroups::CPUTimeType name); - static PerfCounter* get_counter(CPUTimeGroups::CPUTimeType name); static void update_counter(CPUTimeGroups::CPUTimeType name, jlong total); static void publish_gc_total_cpu_time(); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index d5dccc820f3..e9143a3c4e3 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -673,7 +673,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // as interpreted so the skeleton frame will be walkable // The correct pc will be set when the skeleton frame is completely filled out // The final pc we store in the loop is wrong and will be overwritten below - frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0) - frame::pc_return_offset; + frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0); callee_parameters = array->element(index)->method()->size_of_parameters(); callee_locals = array->element(index)->method()->max_locals(); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index b983b4648d4..2b0dd59deba 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -206,9 +206,9 @@ address frame::raw_pc() const { if (is_deoptimized_frame()) { nmethod* nm = cb()->as_nmethod_or_null(); assert(nm != nullptr, "only nmethod is expected here"); - return nm->deopt_handler_entry() - pc_return_offset; + return nm->deopt_handler_entry(); } else { - return (pc() - pc_return_offset); + return pc(); } } @@ -387,6 +387,22 @@ void frame::deoptimize(JavaThread* thread) { #endif // ASSERT } +void frame::deoptimize(JavaThread* thread, stackChunkOop chunk) { + assert(is_heap_frame() && _frame_index >= 0, "wrong frame type"); + + // Fast path does not expect deopted frames + chunk->force_slow_path(); + + frame fr = chunk->derelativize(*this); + fr.deoptimize(nullptr); + + // Fix chunk pc if deopted frame is the top one + bool is_top = fr.sp() == chunk->sp_address(); + if (is_top) { + chunk->set_pc(fr.raw_pc()); + } +} + frame frame::java_sender() const { RegisterMap map(JavaThread::current(), RegisterMap::UpdateMap::skip, @@ -484,23 +500,16 @@ intptr_t* frame::interpreter_frame_local_at(int index) const { } intptr_t* frame::interpreter_frame_expression_stack_at(jint offset) const { - const int i = offset * interpreter_frame_expression_stack_direction(); - const int n = i * Interpreter::stackElementWords; - return &(interpreter_frame_expression_stack()[n]); + const int n = offset * Interpreter::stackElementWords; + return interpreter_frame_expression_stack() - n; } jint frame::interpreter_frame_expression_stack_size() const { // Number of elements on the interpreter expression stack // Callers should span by stackElementWords int element_size = Interpreter::stackElementWords; - size_t stack_size = 0; - if (frame::interpreter_frame_expression_stack_direction() < 0) { - stack_size = (interpreter_frame_expression_stack() - - interpreter_frame_tos_address() + 1)/element_size; - } else { - stack_size = (interpreter_frame_tos_address() - - interpreter_frame_expression_stack() + 1)/element_size; - } + size_t stack_size = (interpreter_frame_expression_stack() - + interpreter_frame_tos_address() + 1)/element_size; assert(stack_size <= (size_t)max_jint, "stack size too big"); return (jint)stack_size; } @@ -775,14 +784,8 @@ class InterpreterFrameClosure : public OffsetClosure { } else { addr = (oop*) _fr->interpreter_frame_expression_stack_at((offset - _max_locals)); // In case of exceptions, the expression stack is invalid and the esp will be reset to express - // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp for Intel). - bool in_stack; - if (frame::interpreter_frame_expression_stack_direction() > 0) { - in_stack = (intptr_t*)addr <= _fr->interpreter_frame_tos_address(); - } else { - in_stack = (intptr_t*)addr >= _fr->interpreter_frame_tos_address(); - } - if (in_stack) { + // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp). + if ((intptr_t*)addr >= _fr->interpreter_frame_tos_address()) { _f->do_oop(addr); } } @@ -1669,13 +1672,13 @@ void FrameValues::print_on(outputStream* st, int min_index, int max_index, intpt // 4. Recognize it as being part of the "fixed frame". if (*fv.location != 0 && *fv.location > -100 && *fv.location < 100 && fp != nullptr && *fv.description != '#' -#if !defined(PPC64) +#if !defined(PPC64) && !defined(S390) && (strncmp(fv.description, "interpreter_frame_", 18) == 0 || strstr(fv.description, " method ")) -#else // !defined(PPC64) +#else // !defined(PPC64) && !defined(S390) && (strcmp(fv.description, "sender_sp") == 0 || strcmp(fv.description, "top_frame_sp") == 0 || strcmp(fv.description, "esp") == 0 || strcmp(fv.description, "monitors") == 0 || strcmp(fv.description, "locals") == 0 || strstr(fv.description, " method ")) -#endif //!defined(PPC64) +#endif // !defined(PPC64) && !defined(S390) ) { st->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT " %-32s (relativized: fp%+d)", p2i(fv.location), p2i(&fp[*fv.location]), fv.description, (int)*fv.location); diff --git a/src/hotspot/share/runtime/frame.hpp b/src/hotspot/share/runtime/frame.hpp index 35d73e8ef78..a9cfacc1241 100644 --- a/src/hotspot/share/runtime/frame.hpp +++ b/src/hotspot/share/runtime/frame.hpp @@ -282,6 +282,7 @@ class frame { // Support for deoptimization void deoptimize(JavaThread* thread); + void deoptimize(JavaThread* thread, stackChunkOop chunk); // The frame's original SP, before any extension by an interpreted callee; // used for packing debug info into vframeArray objects and vframeArray lookup. diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index ec34305f837..cd34d874a3c 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -253,10 +253,6 @@ const int ObjectAlignmentInBytes = 8; develop(bool, TracePcPatching, false, \ "Trace usage of frame::patch_pc") \ \ - develop(bool, TraceRelocator, false, \ - "Trace the bytecode relocator") \ - \ - \ product(bool, SafepointALot, false, DIAGNOSTIC, \ "Generate a lot of safepoints. This works with " \ "GuaranteedSafepointInterval") \ @@ -465,9 +461,6 @@ const int ObjectAlignmentInBytes = 8; develop(bool, VerifyStackAtCalls, false, \ "Verify that the stack pointer is unchanged after calls") \ \ - develop(bool, TraceJavaAssertions, false, \ - "Trace java language assertions") \ - \ develop(bool, VerifyCodeCache, false, \ "Verify code cache on memory allocation/deallocation") \ \ diff --git a/src/hotspot/share/runtime/hotCodeCollector.cpp b/src/hotspot/share/runtime/hotCodeCollector.cpp index 6bdeee011ce..179b57d4678 100644 --- a/src/hotspot/share/runtime/hotCodeCollector.cpp +++ b/src/hotspot/share/runtime/hotCodeCollector.cpp @@ -97,7 +97,9 @@ void HotCodeCollector::thread_entry(JavaThread* thread, TRAPS) { ThreadSampler sampler; uint64_t start_time = os::javaTimeMillis(); while (os::javaTimeMillis() - start_time <= HotCodeSampleSeconds * 1000) { - sampler.sample_all_java_threads(); + if (!sampler.sample_all_java_threads()) { + break; + } thread->sleep(rand_sampling_period_ms()); } diff --git a/src/hotspot/share/runtime/hotCodeSampler.cpp b/src/hotspot/share/runtime/hotCodeSampler.cpp index 730a47d238a..e033765c1f2 100644 --- a/src/hotspot/share/runtime/hotCodeSampler.cpp +++ b/src/hotspot/share/runtime/hotCodeSampler.cpp @@ -29,7 +29,13 @@ #include "runtime/hotCodeSampler.hpp" #include "runtime/javaThread.inline.hpp" -void ThreadSampler::sample_all_java_threads() { +#if INCLUDE_JFR +#include "jfr/utilities/jfrTryLock.hpp" + +using SuspendedThreadTaskTryLock = JfrMutexTryLock; +#endif + +bool ThreadSampler::sample_all_java_threads() { // Collect samples for each JavaThread for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { if (jt->is_hidden_from_external_view() || @@ -39,24 +45,33 @@ void ThreadSampler::sample_all_java_threads() { } GetPCTask task(jt); - task.run(); + { +#if INCLUDE_JFR + SuspendedThreadTaskTryLock try_lock(SuspendedThreadTask_lock); + if (!try_lock.acquired()) { + log_debug(hotcode)("Suspend lock held by JFR sampler; stopping this sampling round, will retry after %u seconds", HotCodeIntervalSeconds); + return false; + } +#endif + 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(); - } + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb != nullptr && cb->is_nmethod()) { + bool created = false; + int *count = _samples.put_if_absent(cb->as_nmethod(), 0, &created); + (*count)++; + if (created) { + _samples.maybe_grow(); } } } + return true; } Candidates::Candidates(ThreadSampler& sampler) diff --git a/src/hotspot/share/runtime/hotCodeSampler.hpp b/src/hotspot/share/runtime/hotCodeSampler.hpp index d61cac791e1..e6c2474b068 100644 --- a/src/hotspot/share/runtime/hotCodeSampler.hpp +++ b/src/hotspot/share/runtime/hotCodeSampler.hpp @@ -90,8 +90,8 @@ class ThreadSampler : public StackObj { public: ThreadSampler() : _samples(INITIAL_TABLE_SIZE, HotCodeSampleSeconds * 1000 / HotCodeMaxSamplingMs) {} - // Iterate over and sample all Java threads - void sample_all_java_threads(); + // Iterate over and sample all Java threads. Return false if sampling was interrupted by JFR sampling. + bool sample_all_java_threads(); // Iterate over all samples with a callback function template diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 32e7208474d..c9fa936f203 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -104,8 +104,8 @@ Mutex* G1MarkStackChunkList_lock = nullptr; Mutex* G1MarkStackFreeList_lock = nullptr; Monitor* G1OldGCCount_lock = nullptr; Mutex* G1OldSets_lock = nullptr; -Mutex* G1ReviseYoungLength_lock = nullptr; Mutex* G1RareEvent_lock = nullptr; +Mutex* G1ReviseNumYoungRegions_lock = nullptr; Mutex* G1Uncommit_lock = nullptr; #endif @@ -121,6 +121,7 @@ Mutex* Verify_lock = nullptr; Mutex* JfrStacktrace_lock = nullptr; Monitor* JfrMsg_lock = nullptr; Mutex* JfrBuffer_lock = nullptr; +Mutex* SuspendedThreadTask_lock = nullptr; #endif Mutex* CodeHeapStateAnalytics_lock = nullptr; @@ -280,6 +281,7 @@ void mutex_init() { MUTEX_DEFN(JfrBuffer_lock , PaddedMutex , event); MUTEX_DEFN(JfrMsg_lock , PaddedMonitor, event); MUTEX_DEFN(JfrStacktrace_lock , PaddedMutex , event); + MUTEX_DEFN(SuspendedThreadTask_lock , PaddedMutex , nosafepoint); #endif MUTEX_DEFN(ContinuationRelativize_lock , PaddedMonitor, nosafepoint-3); @@ -333,7 +335,7 @@ void mutex_init() { if (UseG1GC) { MUTEX_DEFL(G1OldGCCount_lock , PaddedMonitor, Threads_lock, true); MUTEX_DEFL(G1RareEvent_lock , PaddedMutex , Threads_lock, true); - MUTEX_DEFL(G1ReviseYoungLength_lock , PaddedMutex , Threads_lock, true); + MUTEX_DEFL(G1ReviseNumYoungRegions_lock , PaddedMutex , Threads_lock, true); } #endif diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 044fcb732af..ae9c5e8a1f1 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -100,7 +100,7 @@ extern Mutex* G1MarkStackFreeList_lock; // Protects access to the G1 gl extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc 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 Mutex* G1ReviseNumYoungRegions_lock; // Protects access to number of young regions revising operations. extern Mutex* G1Uncommit_lock; // protects the G1 uncommit list when not at safepoints #endif @@ -139,6 +139,7 @@ extern Mutex* FinalImageRecipes_lock; // Protecting the tables used b extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table extern Monitor* JfrMsg_lock; // protects JFR messaging extern Mutex* JfrBuffer_lock; // protects JFR buffer operations +extern Mutex* SuspendedThreadTask_lock; // used to guard SuspendedThreadTask::run #endif extern Mutex* Metaspace_lock; // protects Metaspace virtualspace and chunk expansions diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index f9e3a513a78..ae786bd86c5 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2397,14 +2397,14 @@ void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) { } char* os::reserve_memory_special(size_t size, size_t alignment, size_t page_size, - char* addr, bool executable) { + char* addr, MemTag mem_tag, bool executable) { assert(is_aligned(addr, alignment), "Unaligned request address"); char* result = pd_reserve_memory_special(size, alignment, page_size, addr, executable); if (result != nullptr) { // The memory is committed - MemTracker::record_virtual_memory_reserve_and_commit((address)result, size, CALLER_PC, mtNone); + MemTracker::record_virtual_memory_reserve_and_commit((address)result, size, CALLER_PC, mem_tag); log_debug(os, map)("Reserved and committed " RANGEFMT, RANGEFMTARGS(result, size)); } else { log_info(os, map)("Reserve and commit failed (%zu bytes)", size); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 10a8dd6f858..50e087dcc94 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -257,6 +257,7 @@ class os: AllStatic { static void initialize_initial_active_processor_count(); LINUX_ONLY(static void pd_init_container_support();) + LINUX_ONLY(static void pd_check_temp_directory();) public: static void init(void); // Called before command line parsing @@ -265,6 +266,11 @@ class os: AllStatic { LINUX_ONLY(pd_init_container_support();) } + static void check_temp_directory() { + // Only applicable on linux. + LINUX_ONLY(pd_check_temp_directory();) + } + static void init_before_ergo(void); // Called after command line parsing // before VM ergonomics processing. static jint init_2(void); // Called after command line parsing @@ -601,7 +607,7 @@ class os: AllStatic { static char* non_memory_address_word(); // reserve, commit and pin the entire memory region static char* reserve_memory_special(size_t size, size_t alignment, size_t page_size, - char* addr, bool executable); + char* addr, MemTag mem_tag, bool executable); static void large_page_init(); static size_t large_page_size(); static bool can_commit_large_page_memory(); diff --git a/src/hotspot/share/runtime/relocator.cpp b/src/hotspot/share/runtime/relocator.cpp index ecccea2fbe7..0da545c7ef9 100644 --- a/src/hotspot/share/runtime/relocator.cpp +++ b/src/hotspot/share/runtime/relocator.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 @@ -24,6 +24,8 @@ #include "classfile/stackMapTableFormat.hpp" #include "interpreter/bytecodes.hpp" +#include "logging/logStream.hpp" +#include "logging/logTag.hpp" #include "memory/metadataFactory.hpp" #include "memory/oopFactory.hpp" #include "oops/method.inline.hpp" @@ -50,13 +52,13 @@ class ChangeItem : public ResourceObj { virtual bool is_switch_pad() { return false; } // accessors - int bci() { return _bci; } + int bci() const { return _bci; } void relocate(int break_bci, int delta) { if (_bci > break_bci) { _bci += delta; } } virtual bool adjust(int bci, int delta) { return false; } // debug - virtual void print() = 0; + virtual void print_on(outputStream* st) const = 0; }; class ChangeWiden : public ChangeItem { @@ -71,7 +73,7 @@ class ChangeWiden : public ChangeItem { // Callback to do instruction bool handle_code_change(Relocator *r) { return r->handle_widen(bci(), _new_ilen, _inst_buffer); }; - void print() { tty->print_cr("ChangeWiden. bci: %d New_ilen: %d", bci(), _new_ilen); } + void print_on(outputStream* st) const { st->print_cr("ChangeWiden. bci: %d New_ilen: %d", bci(), _new_ilen); } }; class ChangeJumpWiden : public ChangeItem { @@ -94,7 +96,7 @@ class ChangeJumpWiden : public ChangeItem { return false; } - void print() { tty->print_cr("ChangeJumpWiden. bci: %d Delta: %d", bci(), _delta); } + void print_on(outputStream* st) const { st->print_cr("ChangeJumpWiden. bci: %d Delta: %d", bci(), _delta); } }; class ChangeSwitchPad : public ChangeItem { @@ -113,7 +115,9 @@ class ChangeSwitchPad : public ChangeItem { int padding() { return _padding; } bool is_lookup_switch() { return _is_lookup_switch; } - void print() { tty->print_cr("ChangeSwitchPad. bci: %d Padding: %d IsLookupSwitch: %d", bci(), _padding, _is_lookup_switch); } + void print_on(outputStream* st) const { + st->print_cr("ChangeSwitchPad. bci: %d Padding: %d IsLookupSwitch: %d", bci(), _padding, _is_lookup_switch); + } }; //----------------------------------------------------------------------------------------------------------- @@ -140,11 +144,10 @@ methodHandle Relocator::insert_space_at(int bci, int size, u_char inst_buffer[], _changes = new GrowableArray (10); _changes->push(new ChangeWiden(bci, size, inst_buffer)); - if (TraceRelocator) { - tty->print_cr("Space at: %d Size: %d", bci, size); - _method->print(); - _method->print_codes(); - tty->print_cr("-------------------------------------------------"); + if (const LogTarget(Debug, relocator) out; out.is_enabled()) { + LogStream ls(out); + ls.print_cr("Space at: %d Size: %d", bci, size); + _method->print_value_on(&ls); } if (!handle_code_changes()) return methodHandle(); @@ -160,13 +163,7 @@ methodHandle Relocator::insert_space_at(int bci, int size, u_char inst_buffer[], ClassLoaderData* loader_data = method()->method_holder()->class_loader_data(); loader_data->add_to_deallocate_list(method()()); - set_method(new_method); - - if (TraceRelocator) { - tty->print_cr("-------------------------------------------------"); - tty->print_cr("new method"); - _method->print_codes(); - } + set_method(new_method); return new_method; } @@ -179,8 +176,9 @@ bool Relocator::handle_code_changes() { // Inv: everything is aligned. ChangeItem* ci = _changes->first(); - if (TraceRelocator) { - ci->print(); + if (const LogTarget(Trace, relocator) out; out.is_enabled()) { + LogStream ls(out); + ci->print_on(&ls); } // Execute operation @@ -407,13 +405,13 @@ void Relocator::adjust_exception_table(int bci, int delta) { } } -static void print_linenumber_table(unsigned char* table) { +static void print_linenumber_table(outputStream* ls, unsigned char* table) { CompressedLineNumberReadStream stream(table); - tty->print_cr("-------------------------------------------------"); + ls->print_cr("-------------------------------------------------"); while (stream.read_pair()) { - tty->print_cr(" - line %d: %d", stream.line(), stream.bci()); + ls->print_cr(" - line %d: %d", stream.line(), stream.bci()); } - tty->print_cr("-------------------------------------------------"); + ls->print_cr("-------------------------------------------------"); } // The width of instruction at "bci" is changing by "delta". Adjust the line number table. @@ -433,9 +431,10 @@ void Relocator::adjust_line_no_table(int bci, int delta) { writer.write_terminator(); set_compressed_line_number_table(writer.buffer()); set_compressed_line_number_table_size(writer.position()); - if (TraceRelocator) { - tty->print_cr("Adjusted line number table"); - print_linenumber_table(compressed_line_number_table()); + if (LogMessage(relocator) out; out.is_trace()) { + NonInterleavingLogStream ls(LogLevelType::Trace, out); + ls.print_cr("Adjusted line number table"); + print_linenumber_table(&ls, compressed_line_number_table()); } } } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index b799063d58e..919161dde2f 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -108,14 +108,6 @@ nmethod* SharedRuntime::_cont_doYield_stub; -#if 0 -// TODO tweak global stub name generation to match this -#define SHARED_STUB_NAME_DECLARE(name, type) "Shared Runtime " # name "_blob", -const char *SharedRuntime::_stub_names[] = { - SHARED_STUBS_DO(SHARED_STUB_NAME_DECLARE) -}; -#endif - //----------------------------generate_stubs----------------------------------- void SharedRuntime::generate_initial_stubs() { // Build this early so it's available for the interpreter. @@ -1810,7 +1802,7 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal nmethod* caller = cb->as_nmethod(); // Get the return PC for the passed caller PC. - address return_pc = caller_pc + frame::pc_return_offset; + address return_pc = caller_pc; if (!caller->is_in_use() || !NativeCall::is_call_before(return_pc)) { return; @@ -3104,14 +3096,16 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) { struct { double data[20]; } locs_buf; struct { double data[20]; } stubs_locs_buf; buffer.insts()->initialize_shared_locs((relocInfo*)&locs_buf, sizeof(locs_buf) / sizeof(relocInfo)); -#if defined(AARCH64) || defined(PPC64) +#if defined(AARCH64) // On AArch64 with ZGC and nmethod entry barriers, we need all oops to be // in the constant pool to ensure ordering between the barrier and oops // accesses. For native_wrappers we need a constant. - // On PPC64 the continuation enter intrinsic needs the constant pool for the compiled + buffer.initialize_consts_size(8); +#elif defined(PPC64) || defined(S390) + // On PPC64/S390 the continuation enter intrinsic needs the constant pool for the compiled // static java call that is resolved in the runtime. - if (PPC64_ONLY(method->is_continuation_enter_intrinsic() &&) true) { - buffer.initialize_consts_size(8 PPC64_ONLY(+ 24)); + if (method->is_continuation_enter_intrinsic()) { + buffer.initialize_consts_size(8 PPC64_ONLY(+ 24) S390_ONLY(+ 17)); } #endif buffer.stubs()->initialize_shared_locs((relocInfo*)&stubs_locs_buf, sizeof(stubs_locs_buf) / sizeof(relocInfo)); diff --git a/src/hotspot/share/runtime/vframe.cpp b/src/hotspot/share/runtime/vframe.cpp index c9628255e45..02386537004 100644 --- a/src/hotspot/share/runtime/vframe.cpp +++ b/src/hotspot/share/runtime/vframe.cpp @@ -318,13 +318,9 @@ static StackValue* create_stack_value_from_oop_map(const InterpreterOopMap& oop_ static bool is_in_expression_stack(const frame& fr, const intptr_t* const addr) { assert(addr != nullptr, "invariant"); - // Ensure to be 'inside' the expression stack (i.e., addr >= sp for Intel). + // Ensure to be 'inside' the expression stack (i.e., addr >= sp). // In case of exceptions, the expression stack is invalid and the sp // will be reset to express this condition. - if (frame::interpreter_frame_expression_stack_direction() > 0) { - return addr <= fr.interpreter_frame_tos_address(); - } - return addr >= fr.interpreter_frame_tos_address(); } diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index 6810d7bb8d3..0885262eefb 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -473,12 +473,7 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, "expression stack size should have been extended"); #endif // ASSERT int top_element = iframe()->interpreter_frame_expression_stack_size()-1; - intptr_t* base; - if (frame::interpreter_frame_expression_stack_direction() < 0) { - base = iframe()->interpreter_frame_expression_stack_at(top_element); - } else { - base = iframe()->interpreter_frame_expression_stack(); - } + intptr_t* base = iframe()->interpreter_frame_expression_stack_at(top_element); Copy::conjoint_jbytes(saved_args, base, popframe_preserved_args_size_in_bytes); diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index e22d11cf1a8..af9aa68c7ec 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -59,6 +59,7 @@ template(G1PauseCleanup) \ template(G1TryInitiateConcMark) \ template(G1RendezvousGCThreads) \ + template(G1StopMarking) \ template(ZMarkEndOld) \ template(ZMarkEndYoung) \ template(ZMarkFlushOperation) \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 856ff947dc4..3868510691a 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1709,8 +1709,6 @@ /**********************/ \ NOT_ZERO(PPC64_ONLY(declare_constant(frame::entry_frame_locals_size))) \ \ - declare_constant(frame::pc_return_offset) \ - \ /*************/ \ /* vmSymbols */ \ /*************/ \ diff --git a/src/hotspot/share/services/attachListener.cpp b/src/hotspot/share/services/attachListener.cpp index 92d3c302ded..b4e9bd88c1b 100644 --- a/src/hotspot/share/services/attachListener.cpp +++ b/src/hotspot/share/services/attachListener.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 @@ -180,7 +180,12 @@ static bool get_bool_sys_prop(const char* name, bool default_value, TRAPS) { HandleMark hm(THREAD); // setup the arguments to getProperty - Handle key_str = java_lang_String::create_from_str(name, CHECK_(default_value)); + Handle key_str = java_lang_String::create_from_str(name, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return default_value; + } + // return value JavaValue result(T_OBJECT); // public static String getProperty(String key, String def); @@ -189,7 +194,12 @@ static bool get_bool_sys_prop(const char* name, bool default_value, TRAPS) { vmSymbols::getProperty_name(), vmSymbols::string_string_signature(), key_str, - CHECK_(default_value)); + THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return default_value; + } + oop value_oop = result.get_oop(); if (value_oop != nullptr) { // convert Java String to utf8 string diff --git a/src/hotspot/share/services/memoryPool.cpp b/src/hotspot/share/services/memoryPool.cpp index 41362181c6b..8361913862c 100644 --- a/src/hotspot/share/services/memoryPool.cpp +++ b/src/hotspot/share/services/memoryPool.cpp @@ -51,7 +51,6 @@ MemoryPool::MemoryPool(const char* name, _type(type), _initial_size(init_size), _max_size(max_size), - _available_for_allocation(true), _managers(), _num_managers(0), _peak_usage(), @@ -188,7 +187,7 @@ MemoryUsage CodeHeapPool::get_memory_usage() { size_t used = used_in_bytes(); OrderAccess::acquire(); // ensure possible cache expansion in CodeCache::allocate is seen size_t committed = _codeHeap->capacity(); - size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t maxSize = max_size(); return MemoryUsage(initial_size(), used, committed, maxSize); } diff --git a/src/hotspot/share/services/memoryPool.hpp b/src/hotspot/share/services/memoryPool.hpp index a55c593ebed..f0a4ba21659 100644 --- a/src/hotspot/share/services/memoryPool.hpp +++ b/src/hotspot/share/services/memoryPool.hpp @@ -61,7 +61,6 @@ class MemoryPool : public CHeapObj { PoolType _type; size_t _initial_size; size_t _max_size; - bool _available_for_allocation; // Default is true MemoryManager* _managers[max_num_managers]; int _num_managers; MemoryUsage _peak_usage; // Peak memory usage @@ -98,13 +97,6 @@ class MemoryPool : public CHeapObj { bool is_pool(instanceHandle pool) const; - bool available_for_allocation() { return _available_for_allocation; } - bool set_available_for_allocation(bool value) { - bool prev = _available_for_allocation; - _available_for_allocation = value; - return prev; - } - MemoryManager* get_memory_manager(int index) { assert(index >= 0 && index < _num_managers, "Invalid index"); return _managers[index]; diff --git a/src/hotspot/share/utilities/devirtualizer.hpp b/src/hotspot/share/utilities/devirtualizer.hpp index b4d444dc5a8..39e1ba89239 100644 --- a/src/hotspot/share/utilities/devirtualizer.hpp +++ b/src/hotspot/share/utilities/devirtualizer.hpp @@ -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 @@ -34,12 +34,23 @@ class ClassLoaderData; // a concrete implementation, otherwise a virtual call is taken. class Devirtualizer { public: - template static void do_oop(OopClosureType* closure, T* p); - template static void do_klass(OopClosureType* closure, Klass* k); - template static void do_cld(OopClosureType* closure, ClassLoaderData* cld); - template static bool do_metadata(OopClosureType* closure); - template static void do_derived_oop(DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived); - template static bool do_bit(BitMapClosureType* closure, BitMap::idx_t index); + template + static ALWAYSINLINE void do_oop(OopClosureType* closure, T* p); + + template + static ALWAYSINLINE void do_klass(OopClosureType* closure, Klass* k); + + template + static ALWAYSINLINE void do_cld(OopClosureType* closure, ClassLoaderData* cld); + + template + static ALWAYSINLINE bool do_metadata(OopClosureType* closure); + + template + static ALWAYSINLINE void do_derived_oop(DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived); + + template + static ALWAYSINLINE bool do_bit(BitMapClosureType* closure, BitMap::idx_t index); }; #endif // SHARE_UTILITIES_DEVIRTUALIZER_HPP diff --git a/src/hotspot/share/utilities/devirtualizer.inline.hpp b/src/hotspot/share/utilities/devirtualizer.inline.hpp index 7f49524e0fb..8cc6f931908 100644 --- a/src/hotspot/share/utilities/devirtualizer.inline.hpp +++ b/src/hotspot/share/utilities/devirtualizer.inline.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 @@ -74,12 +74,14 @@ // p - The oop (or narrowOop) field to pass to the closure template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { closure->do_oop(p); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { // Sanity check @@ -95,12 +97,14 @@ inline void Devirtualizer::do_oop(OopClosureType* closure, T* p) { // Implementation of the non-virtual do_metadata dispatch. template +ALWAYSINLINE static typename EnableIf::value, bool>::type call_do_metadata(bool (Receiver::*)(), bool (Base::*)(), OopClosureType* closure) { return closure->do_metadata(); } template +ALWAYSINLINE static typename EnableIf::value, bool>::type call_do_metadata(bool (Receiver::*)(), bool (Base::*)(), OopClosureType* closure) { return closure->OopClosureType::do_metadata(); @@ -114,12 +118,14 @@ inline bool Devirtualizer::do_metadata(OopClosureType* closure) { // Implementation of the non-virtual do_klass dispatch. template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_klass(void (Receiver::*)(Klass*), void (Base::*)(Klass*), OopClosureType* closure, Klass* k) { closure->do_klass(k); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_klass(void (Receiver::*)(Klass*), void (Base::*)(Klass*), OopClosureType* closure, Klass* k) { closure->OopClosureType::do_klass(k); @@ -133,12 +139,14 @@ inline void Devirtualizer::do_klass(OopClosureType* closure, Klass* k) { // Implementation of the non-virtual do_cld dispatch. template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_cld(void (Receiver::*)(ClassLoaderData*), void (Base::*)(ClassLoaderData*), OopClosureType* closure, ClassLoaderData* cld) { closure->do_cld(cld); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_cld(void (Receiver::*)(ClassLoaderData*), void (Base::*)(ClassLoaderData*), OopClosureType* closure, ClassLoaderData* cld) { closure->OopClosureType::do_cld(cld); @@ -152,12 +160,14 @@ void Devirtualizer::do_cld(OopClosureType* closure, ClassLoaderData* cld) { // Implementation of the non-virtual do_derived_oop dispatch. template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_derived_oop(void (Receiver::*)(derived_base*, derived_pointer*), void (Base::*)(derived_base*, derived_pointer*), DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived) { closure->do_derived_oop(base, derived); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_derived_oop(void (Receiver::*)(derived_base*, derived_pointer*), void (Base::*)(derived_base*, derived_pointer*), DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived) { closure->DerivedOopClosureType::do_derived_oop(base, derived); diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 40691de518e..5e5a57c3780 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1157,8 +1157,8 @@ inline T clamp(T value, T min, T max) { return MIN2(MAX2(value, min), max); } -inline bool is_odd (intx x) { return x & 1; } -inline bool is_even(intx x) { return !is_odd(x); } +constexpr bool is_odd (intx x) { return x & 1; } +constexpr bool is_even(intx x) { return !is_odd(x); } // abs methods which cannot overflow and so are well-defined across // the entire domain of integer types. diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java index 96a1eb686cc..5335357f8b9 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java @@ -858,7 +858,7 @@ public final class ML_KEM { allDone = false; while (!allDone) { allDone = true; - parXof.squeezeBlock(); + parXof.squeezeBlock(parInd); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index b08b9fe4d2c..b8bc97a1f0d 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -233,8 +233,7 @@ public final class Class implements java.io.Serializable, runtimeSetup(); } - /// No significant static final fields; [#resetArchivedStates()] handles - /// prevents storing [#reflectionFactory] into AOT image. + /// No significant static final fields @AOTRuntimeSetup private static void runtimeSetup() { registerNatives(); @@ -710,7 +709,7 @@ public final class Class implements java.io.Serializable, } try { Class[] empty = {}; - final Constructor c = getReflectionFactory().copyConstructor( + final Constructor c = ReflectionFactory.getReflectionFactory().copyConstructor( getConstructor0(empty, Member.DECLARED)); // Disable accessibility checks on the constructor // access check is done with the true caller @@ -724,7 +723,8 @@ public final class Class implements java.io.Serializable, try { Class caller = Reflection.getCallerClass(); - return getReflectionFactory().newInstance(tmpConstructor, null, caller); + return ReflectionFactory.getReflectionFactory().newInstance(tmpConstructor, + null, caller); } catch (InvocationTargetException e) { Unsafe.getUnsafe().throwException(e.getTargetException()); // Not reached @@ -1396,8 +1396,9 @@ public final class Class implements java.io.Serializable, isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : AccessFlag.Location.CLASS; - return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ? - getClassFileAccessFlags() : getModifiers(), location, this); + return ReflectionFactory.getReflectionFactory().parseAccessFlags( + (location == AccessFlag.Location.CLASS) ? getClassFileAccessFlags() : getModifiers(), + location, this); } /** @@ -1460,7 +1461,7 @@ public final class Class implements java.io.Serializable, * type. Matching return type is also necessary * because of covariant returns, etc. */ - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (Method m : candidates) { if (m.getName().equals(enclosingInfo.getName()) && arrayContentsEq(parameterClasses, @@ -1586,7 +1587,7 @@ public final class Class implements java.io.Serializable, * Loop over all declared constructors; match number * of and type of parameters. */ - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (Constructor c : candidates) { if (arrayContentsEq(parameterClasses, fact.getExecutableSharedParameterTypes(c))) { @@ -2069,7 +2070,7 @@ public final class Class implements java.io.Serializable, if (field == null) { throw new NoSuchFieldException(name); } - return getReflectionFactory().copyField(field); + return ReflectionFactory.getReflectionFactory().copyField(field); } @@ -2167,7 +2168,7 @@ public final class Class implements java.io.Serializable, if (method == null) { throw new NoSuchMethodException(methodToString(name, parameterTypes)); } - return getReflectionFactory().copyMethod(method); + return ReflectionFactory.getReflectionFactory().copyMethod(method); } /** @@ -2198,7 +2199,7 @@ public final class Class implements java.io.Serializable, */ public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException { - return getReflectionFactory().copyConstructor( + return ReflectionFactory.getReflectionFactory().copyConstructor( getConstructor0(parameterTypes, Member.PUBLIC)); } @@ -2383,7 +2384,7 @@ public final class Class implements java.io.Serializable, if (field == null) { throw new NoSuchFieldException(name); } - return getReflectionFactory().copyField(field); + return ReflectionFactory.getReflectionFactory().copyField(field); } @@ -2425,7 +2426,7 @@ public final class Class implements java.io.Serializable, if (method == null) { throw new NoSuchMethodException(methodToString(name, parameterTypes)); } - return getReflectionFactory().copyMethod(method); + return ReflectionFactory.getReflectionFactory().copyMethod(method); } /** @@ -2440,7 +2441,7 @@ public final class Class implements java.io.Serializable, */ List getDeclaredPublicMethods(String name, Class... parameterTypes) { Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true); - ReflectionFactory factory = getReflectionFactory(); + ReflectionFactory factory = ReflectionFactory.getReflectionFactory(); List result = new ArrayList<>(); for (Method method : methods) { if (method.getName().equals(name) @@ -2465,7 +2466,8 @@ public final class Class implements java.io.Serializable, */ Method findMethod(boolean publicOnly, String name, Class... parameterTypes) { PublicMethods.MethodList res = getMethodsRecursive(name, parameterTypes, true, publicOnly); - return res == null ? null : getReflectionFactory().copyMethod(res.getMostSpecific()); + return res == null ? null : ReflectionFactory.getReflectionFactory().copyMethod( + res.getMostSpecific()); } /** @@ -2492,7 +2494,7 @@ public final class Class implements java.io.Serializable, */ public Constructor getDeclaredConstructor(Class... parameterTypes) throws NoSuchMethodException { - return getReflectionFactory().copyConstructor( + return ReflectionFactory.getReflectionFactory().copyConstructor( getConstructor0(parameterTypes, Member.DECLARED)); } @@ -2897,7 +2899,7 @@ public final class Class implements java.io.Serializable, // Since 1.8 native byte[] getRawTypeAnnotations(); static byte[] getExecutableTypeAnnotationBytes(Executable ex) { - return getReflectionFactory().getExecutableTypeAnnotationBytes(ex); + return ReflectionFactory.getReflectionFactory().getExecutableTypeAnnotationBytes(ex); } native ConstantPool getConstantPool(); @@ -3111,7 +3113,7 @@ public final class Class implements java.io.Serializable, String name, Class[] parameterTypes) { - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); Method res = null; for (Method m : methods) { if (m.getName().equals(name) @@ -3179,7 +3181,7 @@ public final class Class implements java.io.Serializable, private Constructor getConstructor0(Class[] parameterTypes, int which) throws NoSuchMethodException { - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); Constructor[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); for (Constructor constructor : constructors) { if (arrayContentsEq(parameterTypes, @@ -3218,7 +3220,7 @@ public final class Class implements java.io.Serializable, private static Field[] copyFields(Field[] arg) { Field[] out = new Field[arg.length]; - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (int i = 0; i < arg.length; i++) { out[i] = fact.copyField(arg[i]); } @@ -3227,7 +3229,7 @@ public final class Class implements java.io.Serializable, private static Method[] copyMethods(Method[] arg) { Method[] out = new Method[arg.length]; - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (int i = 0; i < arg.length; i++) { out[i] = fact.copyMethod(arg[i]); } @@ -3236,7 +3238,7 @@ public final class Class implements java.io.Serializable, private static Constructor[] copyConstructors(Constructor[] arg) { Constructor[] out = arg.clone(); - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (int i = 0; i < out.length; i++) { out[i] = fact.copyConstructor(out[i]); } @@ -3390,25 +3392,6 @@ public final class Class implements java.io.Serializable, isRecord0(); } - // Fetches the factory for reflective objects - private static ReflectionFactory getReflectionFactory() { - var factory = reflectionFactory; - if (factory != null) { - return factory; - } - return reflectionFactory = ReflectionFactory.getReflectionFactory(); - } - private static ReflectionFactory reflectionFactory; - - /** - * When CDS is enabled, the Class class may be aot-initialized. However, - * we can't archive reflectionFactory, so we reset it to null, so it - * will be allocated again at runtime. - */ - private static void resetArchivedStates() { - reflectionFactory = null; - } - /** * Returns the elements of this enum class or null if this * Class object does not represent an enum class. diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 760f3ebc255..e3d120c23f6 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -671,14 +671,6 @@ public final class String } private static String decode(Charset charset, byte[] bytes, int offset, int length) { - // (1)We never cache the "external" cs, the only benefit of creating - // an additional StringDe/Encoder object to wrap it is to share the - // de/encode() method. These SD/E objects are short-lived, the young-gen - // gc should be able to take care of them well. But the best approach - // is still not to generate them if not really necessary. - // (2)The defensive copy of the input byte/char[] has a big performance - // impact, as well as the outgoing result byte/char[]. Need to do the - // optimization check of (sm==null && classLoader0==null) for both. CharsetDecoder cd = charset.newDecoder(); // ArrayDecoder fastpaths if (cd instanceof ArrayDecoder ad) { @@ -4062,7 +4054,7 @@ public final class String * (all) * * ΙΧΘΥΣ - * ιχθυσ + * ιχθυς * lowercased all chars in String * * diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template index aa8c7b28617..0147810cb4e 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template @@ -33,20 +33,20 @@ import static java.lang.invoke.SegmentVarHandle.*; #warn -{#if[byte]?final:sealed} class VarHandleSegmentAs$Type$s { +{#if[ByteOrBoolean]?final:sealed} class VarHandleSegmentAs$Type$s { -#if[!byte] +#if[!ByteOrBoolean] static final int NON_PLAIN_ACCESS_MIN_ALIGN_MASK = $BoxType$.BYTES - 1; -#end[byte] +#end[ByteOrBoolean] static VarForm selectForm(long alignmentMask, boolean constantOffset) { -#if[byte] +#if[ByteOrBoolean] return constantOffset ? CONSTANT_OFFSET_FORM : VARIABLE_OFFSET_FORM; -#else[byte] +#else[ByteOrBoolean] return (alignmentMask & NON_PLAIN_ACCESS_MIN_ALIGN_MASK) != NON_PLAIN_ACCESS_MIN_ALIGN_MASK ? (constantOffset ? CONSTANT_OFFSET_FORM : VARIABLE_OFFSET_FORM) : (constantOffset ? VarHandleSegmentAs$Type$sAligned.CONSTANT_OFFSET_FORM : VarHandleSegmentAs$Type$sAligned.VARIABLE_OFFSET_FORM); -#end[byte] +#end[ByteOrBoolean] } static final VarForm CONSTANT_OFFSET_FORM = new VarForm(VarHandleSegmentAs$Type$s.class, MemorySegment.class, $type$.class, long.class); @@ -70,16 +70,16 @@ import static java.lang.invoke.SegmentVarHandle.*; handle.be); return $Type$.$rawType$BitsTo$Type$(rawValue); #else[floatingPoint] -#if[byte] - return SCOPED_MEMORY_ACCESS.get$Type$(bb.sessionImpl(), +#if[ByteOrBoolean] + return SCOPED_MEMORY_ACCESS.get$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, offset)); -#else[byte] + offset(bb, base, offset)){#if[boolean]? != 0}; +#else[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), handle.be); -#end[byte] +#end[ByteOrBoolean] #end[floatingPoint] } @@ -99,21 +99,21 @@ import static java.lang.invoke.SegmentVarHandle.*; $Type$.$type$ToRaw$RawType$Bits(value), handle.be); #else[floatingPoint] -#if[byte] +#if[ByteOrBoolean] SCOPED_MEMORY_ACCESS.put$Type$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#else[byte] +#else[ByteOrBoolean] SCOPED_MEMORY_ACCESS.put$Type$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value, handle.be); -#end[byte] +#end[ByteOrBoolean] #end[floatingPoint] } -#if[!byte] +#if[!ByteOrBoolean] } // This class must be accessed through non-aligned VarHandleSegmentAs$Type$s @@ -123,7 +123,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static final VarForm VARIABLE_OFFSET_FORM = new VarForm(VarHandleSegmentAs$Type$sAligned.class, VarHandleSegmentAs$Type$s.VARIABLE_OFFSET_FORM); VarHandleSegmentAs$Type$sAligned() { throw new AssertionError(); } -#end[byte] +#end[ByteOrBoolean] #if[floatingPoint] @ForceInline @@ -138,17 +138,29 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { return $Type$.$rawType$BitsTo$Type$(rv); } #else[floatingPoint] -#if[byte] +#if[ByteOrBoolean] +#if[boolean] + @ForceInline + static $rawType$ convEndian(boolean big, $type$ v) { + return (byte) (v ? 1 : 0); + } + + @ForceInline + static $type$ convEndian(boolean big, $rawType$ n) { + return n != 0; + } +#else[boolean] @ForceInline static $type$ convEndian(boolean big, $type$ n) { return n; } -#else[byte] +#end[boolean] +#else[ByteOrBoolean] @ForceInline static $type$ convEndian(boolean big, $type$ n) { return big == BE ? n : $BoxType$.reverseBytes(n); } -#end[byte] +#end[ByteOrBoolean] #end[floatingPoint] @ForceInline @@ -424,18 +436,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndAdd(VarHandle ob, Object obb, long base, long offset, $type$ delta) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), delta); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndAddConvEndianWithCAS(bb, offset(bb, base, offset), delta); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -447,18 +459,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndAddAcquire(VarHandle ob, Object obb, long base, long offset, $type$ delta) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), delta); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndAddConvEndianWithCAS(bb, offset(bb, base, offset), delta); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -470,20 +482,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndAddRelease(VarHandle ob, Object obb, long base, long offset, $type$ delta) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), delta); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndAddConvEndianWithCAS(bb, offset(bb, base, offset), delta); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndAddConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ delta) { @@ -496,7 +508,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue + delta)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] #end[AtomicAdd] #if[Bitwise] @@ -509,18 +521,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseOr(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -532,18 +544,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseOrRelease(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -555,20 +567,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndBitwiseOrConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ value) { @@ -581,7 +593,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue | value)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] @ForceInline static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, long base, $type$ value) { @@ -592,18 +604,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -615,18 +627,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseAndRelease(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -639,20 +651,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndBitwiseAndConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ value) { @@ -665,7 +677,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue & value)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] @ForceInline static $type$ getAndBitwiseXor(VarHandle ob, Object obb, long base, $type$ value) { @@ -676,18 +688,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseXor(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -699,18 +711,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseXorRelease(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -722,20 +734,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndBitwiseXorConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ value) { @@ -748,6 +760,6 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue ^ value)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] #end[Bitwise] } diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index e4b2886404f..922ac651f7e 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -479,12 +479,7 @@ public final class ObjectMethods { * {@link java.lang.Record#toString()}. * * - * @param lookup Every bootstrap method is expected to have a {@code lookup} - * which usually represents a lookup context with the - * accessibility privileges of the caller. This is because - * {@code invokedynamic} call sites always provide a {@code lookup} - * to the corresponding bootstrap method, but this method just - * ignores the {@code lookup} parameter + * @param lookup the full-privilege lookup context of the caller * @param methodName the name of the method to generate, which must be one of * {@code "equals"}, {@code "hashCode"}, or {@code "toString"} * @param type a {@link MethodType} corresponding the descriptor type @@ -503,8 +498,6 @@ public final class ObjectMethods { * if invoked by a condy * @throws IllegalArgumentException if the bootstrap arguments are invalid * or inconsistent - * @throws NullPointerException if any argument is {@code null} or if any element - * in the {@code getters} array is {@code null} * @throws Throwable if any exception is thrown during call site construction */ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type, @@ -518,6 +511,9 @@ public final class ObjectMethods { requireNonNull(names); List getterList = List.of(getters); // deep null check + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + MethodType methodType; if (type instanceof MethodType mt) methodType = mt; diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index d15e701b94d..c52fc9ec75c 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -169,16 +169,13 @@ public final class SwitchBootstraps { * the length of the {@code labels} array (inclusive), * both or an {@link IndexOutOfBoundsException} is thrown. * - * @param lookup Represents a lookup context with the accessibility - * privileges of the caller. When used with {@code invokedynamic}, - * this is stacked automatically by the VM. + * @param lookup the full-privilege lookup context of the caller * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * a target type, an {@code int}, and {@code int} as a return type. * @param labels case labels as described above * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a target type, * second parameter of type {@code int} and with {@code int} as its return type @@ -198,6 +195,9 @@ public final class SwitchBootstraps { requireNonNull(invocationType); requireNonNull(labels); + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + Class selectorType = invocationType.parameterType(0); if (invocationType.parameterCount() != 2 || (!invocationType.returnType().equals(int.class)) @@ -275,9 +275,7 @@ public final class SwitchBootstraps { * @apiNote It is permissible for the {@code labels} array to contain {@code String} * values that do not represent any enum constants at runtime. * - * @param lookup Represents a lookup context with the accessibility - * privileges of the caller. When used with {@code invokedynamic}, - * this is stacked automatically by the VM. + * @param lookup the full-privilege lookup context of the caller * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * an enum type, an {@code int}, and {@code int} as a return type. @@ -285,7 +283,6 @@ public final class SwitchBootstraps { * in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if any element in the labels array is an empty {@code String} * @throws IllegalArgumentException if the invocation type is not a method type @@ -305,6 +302,9 @@ public final class SwitchBootstraps { requireNonNull(invocationType); requireNonNull(labels); + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + if (invocationType.parameterCount() != 2 || (!invocationType.returnType().equals(int.class)) || invocationType.parameterType(0).isPrimitive() diff --git a/src/java.base/share/classes/java/lang/runtime/package-info.java b/src/java.base/share/classes/java/lang/runtime/package-info.java index 9e19ef9bd7e..e2597e45c34 100644 --- a/src/java.base/share/classes/java/lang/runtime/package-info.java +++ b/src/java.base/share/classes/java/lang/runtime/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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,8 +26,20 @@ /** * The {@code java.lang.runtime} package provides low-level runtime support * for the Java language. + *

+ * Unless otherwise specified:

    + *
  • Methods and constructors in this package throw a {@link + * NullPointerException} when they are called with {@code null} or an array + * that contains {@code null} as an argument. + *
  • {@linkplain java.lang.invoke##bsm Bootstrap methods} in this package + * throw an {@link IllegalArgumentException} when they are called with a + * {@link Lookup Lookup} that does not have {@linkplain + * Lookup#hasFullPrivilegeAccess() full privilege access}. + *
* * @since 14 */ package java.lang.runtime; + +import java.lang.invoke.MethodHandles.Lookup; diff --git a/src/java.base/share/classes/java/net/HttpCookie.java b/src/java.base/share/classes/java/net/HttpCookie.java index 3c633522bdf..2b3a5cbb6a5 100644 --- a/src/java.base/share/classes/java/net/HttpCookie.java +++ b/src/java.base/share/classes/java/net/HttpCookie.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 @@ -1005,12 +1005,13 @@ public final class HttpCookie implements Cloneable { } } catch (NumberFormatException ignored) {} - try { - if (expiresValue != null) { - long delta = cookie.expiryDate2DeltaSeconds(expiresValue); + if (expiresValue != null) { + Calendar cal = parseExpires(expiresValue); + if (cal != null) { + long delta = (cal.getTimeInMillis() - cookie.whenCreated) / 1000; cookie.maxAge = (delta > 0 ? delta : 0); } - } catch (NumberFormatException ignored) {} + } } private static void assignAttribute(HttpCookie cookie, @@ -1082,10 +1083,10 @@ public final class HttpCookie implements Cloneable { * @param dateString * a date string in one of the formats defined in Netscape cookie spec * - * @return delta seconds between this cookie's creation time and the time - * specified by dateString + * @return the parsed date as a Calendar, or null if none of the + * formats could parse the given date string */ - private long expiryDate2DeltaSeconds(String dateString) { + private static Calendar parseExpires(String dateString) { Calendar cal = new GregorianCalendar(GMT); for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) { SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], @@ -1108,12 +1109,12 @@ public final class HttpCookie implements Cloneable { } cal.set(Calendar.YEAR, year); } - return (cal.getTimeInMillis() - whenCreated) / 1000; + return cal; } catch (Exception e) { // Ignore, try the next date format } } - return 0; + return null; } /* diff --git a/src/java.base/share/classes/java/security/BinaryEncodable.java b/src/java.base/share/classes/java/security/BinaryEncodable.java index bd5d05ee4ec..a1713c413ba 100644 --- a/src/java.base/share/classes/java/security/BinaryEncodable.java +++ b/src/java.base/share/classes/java/security/BinaryEncodable.java @@ -32,15 +32,35 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import jdk.internal.javac.PreviewFeature; +import sun.security.internal.InternalBinaryEncodable; + /** - * This interface is implemented by security API classes that contain - * binary-encodable cryptographic material. + * This interface identifies the cryptographic objects that can be converted + * to and from binary data, and thereby encoded and decoded as PEM text. * - *

This sealed interface may evolve. When using {@code switch}, always include a - * {@code default} case rather than relying on the classes specified in the - * {@code permits} clause to remain fixed. An exhaustive {@code switch} may - * result in a {@link MatchException}. + *

The APIs for cryptographic objects such as public keys, private keys, + * certificates, and certificate revocation lists all provide the means to + * convert their instances to and from standardized binary representations. + * Other kinds of cryptographic objects, such as certificate requests, have + * no corresponding API but can still be expressed as standardized binary + * representations. The {@code BinaryEncodable} interface allows the + * {@link PEMEncoder} and {@link PEMDecoder} classes to operate uniformly on + * binary representations of key or certificate material. + * + *

The permitted subtype {@code PEM} is notable for supporting the encoding + * and decoding of PEM text that represents cryptographic objects for which no + * API exists. In future releases, other permitted subtypes may be added to + * support the encoding and decoding of such cryptographic objects. + * + *

The list of permitted subtypes shown after {@code permits} is not + * exhaustive. This means if application code switches over a + * {@code BinaryEncodable} value, the {@code switch} cannot be made exhaustive + * simply by providing a {@code case} label for every permitted subtype shown + * in the list; there also must be a {@code default} or + * {@code case BinaryEncodable} label to handle additional subtypes. This + * allows the list of permitted subtypes to change over time without causing + * pre-existing switches to fail because of an unrecognized subtype. * * @see AsymmetricKey * @see KeyPair @@ -57,5 +77,5 @@ import jdk.internal.javac.PreviewFeature; @PreviewFeature(feature = PreviewFeature.Feature.PEM_API) public sealed interface BinaryEncodable permits AsymmetricKey, KeyPair, PKCS8EncodedKeySpec, X509EncodedKeySpec, EncryptedPrivateKeyInfo, - X509Certificate, X509CRL, PEM { + X509Certificate, X509CRL, PEM, InternalBinaryEncodable { } diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 434aa57e3ac..f7adfcbcd62 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -37,6 +37,9 @@ import javax.crypto.SecretKey; import javax.security.auth.DestroyFailedException; import javax.security.auth.callback.*; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + import sun.security.util.Debug; import sun.security.util.CryptoAlgorithmConstraints; @@ -854,8 +857,18 @@ public class KeyStore { *

  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  • * * @@ -876,6 +889,7 @@ public class KeyStore { * * @see Provider */ + @CallerSensitive public static KeyStore getInstance(String type) throws KeyStoreException { @@ -885,6 +899,11 @@ public class KeyStore { throw new KeyStoreException(type + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("KeyStore", type)) { + CryptoAlgorithmConstraints.warn("KeyStore", type, + Reflection.getCallerClass()); + } + try { Object[] objs = Security.getImpl(type, "KeyStore", (String)null); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); @@ -906,11 +925,24 @@ public class KeyStore { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param type the type of keystore. * See the KeyStore section in the + *
  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + * * * @param type the type of keystore. * See the KeyStore section in the
    + *
  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. Disallowed type will be skipped. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + * Disallowed type will be skipped. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + * * * @param file the keystore file * @param password the keystore password, which may be {@code null} @@ -1785,10 +1856,12 @@ public class KeyStore { * * @since 9 */ + @CallerSensitive public static final KeyStore getInstance(File file, char[] password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { - return getInstance(file, password, null, true); + return getInstance(file, password, null, true, + Reflection.getCallerClass()); } /** @@ -1815,11 +1888,25 @@ public class KeyStore { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. Disallowed type will be skipped. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + * Disallowed type will be skipped. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param file the keystore file * @param param the {@code LoadStoreParameter} that specifies how to load @@ -1847,15 +1934,17 @@ public class KeyStore { * * @since 9 */ + @CallerSensitive public static final KeyStore getInstance(File file, LoadStoreParameter param) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { - return getInstance(file, null, param, false); + return getInstance(file, null, param, false, + Reflection.getCallerClass()); } // Used by getInstance(File, char[]) & getInstance(File, LoadStoreParameter) private static final KeyStore getInstance(File file, char[] password, - LoadStoreParameter param, boolean hasPassword) + LoadStoreParameter param, boolean hasPassword, Class callerClass) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { @@ -1893,6 +1982,11 @@ public class KeyStore { String ksAlgo = s.getAlgorithm(); if (CryptoAlgorithmConstraints.permits( "KEYSTORE", ksAlgo)) { + if (CryptoAlgorithmConstraints.isLegacy( + "KeyStore", ksAlgo)) { + CryptoAlgorithmConstraints.warn( + "KeyStore", ksAlgo, callerClass); + } keystore = new KeyStore(impl, p, ksAlgo); } else { matched = ksAlgo; diff --git a/src/java.base/share/classes/java/security/MessageDigest.java b/src/java.base/share/classes/java/security/MessageDigest.java index 6e8f64f7ebe..943459b4bf7 100644 --- a/src/java.base/share/classes/java/security/MessageDigest.java +++ b/src/java.base/share/classes/java/security/MessageDigest.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 @@ -30,6 +30,9 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.ByteBuffer; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + import sun.security.jca.GetInstance; import sun.security.util.Debug; import sun.security.util.MessageDigestSpi2; @@ -168,8 +171,18 @@ public abstract class MessageDigest extends MessageDigestSpi { *
  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  • * * @@ -191,6 +204,7 @@ public abstract class MessageDigest extends MessageDigestSpi { * * @see Provider */ + @CallerSensitive public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -200,6 +214,11 @@ public abstract class MessageDigest extends MessageDigestSpi { throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("MessageDigest", algorithm)) { + CryptoAlgorithmConstraints.warn("MessageDigest", algorithm, + Reflection.getCallerClass()); + } + GetInstance.Instance instance = GetInstance.getInstance("MessageDigest", MessageDigestSpi.class, algorithm); MessageDigest md; @@ -233,11 +252,24 @@ public abstract class MessageDigest extends MessageDigestSpi { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the
    + *
  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + * * * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the
    The {@link #decode(String)} and {@link #decode(InputStream)} methods + *

    The {@link #decode(String)} and {@link #decode(InputStream)} methods * return an instance of a class that matches the PEM type and implements * {@link BinaryEncodable}, as follows: *

      @@ -70,11 +70,18 @@ import java.util.Objects; *
    * *

    If the PEM type has no corresponding class, {@code decode(String)} and - * {@code decode(InputStream)} will return a {@code PEM} object. + * {@code decode(InputStream)} return a {@code PEM} object. + * + *

    If application code switches over the {@code BinaryEncodable} result of + * {@link #decode(String)} or {@link #decode(InputStream)}, the {@code switch} cannot + * be made exhaustive simply by providing a {@code case} label for every permitted + * subtype listed for {@code BinaryEncodable}; there also must be a {@code default} + * or {@code case BinaryEncodable} label to handle additional subtypes that + * might be added in the future. * *

    The {@link #decode(String, Class)} and {@link #decode(InputStream, Class)} - * methods accept a class parameter specifying the desired {@code BinaryEncodable} - * type. These methods avoid the need for casting and are useful when multiple + * methods accept a parameter specifying the desired {@code BinaryEncodable} + * result. These methods avoid the need for casting and are useful when multiple * representations are possible. For example, if the PEM contains both public and * private keys, specifying {@code PrivateKey.class} returns only the private key. * If {@code X509EncodedKeySpec.class} is provided, the public key encoding is @@ -109,11 +116,6 @@ import java.util.Objects; * for decryption, an {@link EncryptedPrivateKeyInfo} is returned. * A {@code PEMDecoder} configured for decryption can also decode unencrypted PEM. * - *

    The {@code BinaryEncodable} interface may evolve. When using a decode method - * with {@code switch}, always include a {@code default} case rather than - * relying on the classes specified in the permits clause to remain fixed. - * An exhaustive {@code switch} may result in a {@link MatchException}. - * *

    This class is immutable and thread-safe. * *

    Example: decode a private key: @@ -136,6 +138,7 @@ import java.util.Objects; * @see PEMEncoder * @see PEM * @see EncryptedPrivateKeyInfo + * @see BinaryEncodable * * @spec https://www.rfc-editor.org/info/rfc1421 * RFC 1421: Privacy Enhancement for Internet Electronic Mail diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index f95caa1d920..e4b6109bfb0 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.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 @@ -1084,9 +1084,16 @@ public abstract class Provider extends Properties { String stdAlg = attrString.substring(0, i).intern(); String attrName = attrString.substring(i + 1); // kill additional spaces - while (attrName.startsWith(" ")) { - attrName = attrName.substring(1); + int pos = 0; + for (; pos < attrName.length(); pos++) { + if (attrName.charAt(pos) != ' ') { + break; + } } + if (pos > 0) { + attrName = attrName.substring(pos); + } + attrName = attrName.intern(); ServiceKey stdKey = new ServiceKey(type, stdAlg, true); Service stdService = legacyMap.get(stdKey); diff --git a/src/java.base/share/classes/java/security/Signature.java b/src/java.base/share/classes/java/security/Signature.java index 228d6fff82b..6606752dde8 100644 --- a/src/java.base/share/classes/java/security/Signature.java +++ b/src/java.base/share/classes/java/security/Signature.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 @@ -38,6 +38,8 @@ import java.security.Provider.Service; import jdk.internal.access.JavaSecuritySignatureAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; import sun.security.util.Debug; import sun.security.util.CryptoAlgorithmConstraints; @@ -237,8 +239,18 @@ public abstract class Signature extends SignatureSpi { *

  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  • * * @@ -259,6 +271,7 @@ public abstract class Signature extends SignatureSpi { * * @see Provider */ + @CallerSensitive public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException { Objects.requireNonNull(algorithm, "null algorithm name"); @@ -267,6 +280,11 @@ public abstract class Signature extends SignatureSpi { throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("Signature", algorithm)) { + CryptoAlgorithmConstraints.warn("Signature", algorithm, + Reflection.getCallerClass()); + } + Iterator t = GetInstance.getServices("Signature", algorithm); if (!t.hasNext()) { throw new NoSuchAlgorithmException @@ -362,11 +380,24 @@ public abstract class Signature extends SignatureSpi { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param algorithm the name of the algorithm requested. * See the Signature section in the
    + *
  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + * * * @param algorithm the name of the algorithm requested. * See the Signature section in the
    + * For example, given a {@code SimpleDateFormat} with a {@code GregorianCalendar}, + * if the start date is set to January 1, 1950, 2-digit years are + * interpreted as falling within the 100-year range from 1950 through 2049. + * In that case, 50 is interpreted as 1950, 99 as 1999, 00 as 2000, and 49 + * as 2049. * - * @param startDate During parsing, two digit years will be placed in the range + * @param startDate During parsing, 2-digit years will be placed in the range * {@code startDate} to {@code startDate + 100 years}. * @see #get2DigitYearStart * @throws NullPointerException if {@code startDate} is {@code null}. @@ -934,11 +939,8 @@ public class SimpleDateFormat extends DateFormat { } /** - * Returns the beginning date of the 100-year period 2-digit years are interpreted - * as being within. + * {@return the start date of the 100-year period used to interpret 2-digit years} * - * @return the start of the 100-year period into which two digit years are - * parsed * @see #set2DigitYearStart * @since 1.2 */ diff --git a/src/java.base/share/classes/java/time/temporal/Temporal.java b/src/java.base/share/classes/java/time/temporal/Temporal.java index e3c0a6819f6..0eb8c28f5ad 100644 --- a/src/java.base/share/classes/java/time/temporal/Temporal.java +++ b/src/java.base/share/classes/java/time/temporal/Temporal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, 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 @@ -392,8 +392,8 @@ public interface Temporal extends TemporalAccessor { * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: *
          *   // these two lines are equivalent
    -     *   temporal = start.until(end, unit);
    -     *   temporal = unit.between(start, end);
    +     *   amount = start.until(end, unit);
    +     *   amount = unit.between(start, end);
          * 
    * The choice should be made based on which makes the code more readable. *

    diff --git a/src/java.base/share/classes/java/time/temporal/TemporalAmount.java b/src/java.base/share/classes/java/time/temporal/TemporalAmount.java index 0d8b8a96bb0..abc694957e1 100644 --- a/src/java.base/share/classes/java/time/temporal/TemporalAmount.java +++ b/src/java.base/share/classes/java/time/temporal/TemporalAmount.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 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 @@ -145,9 +145,9 @@ public interface TemporalAmount { *

          *   // These two lines are equivalent, but the second approach is recommended
          *   dateTime = amount.addTo(dateTime);
    -     *   dateTime = dateTime.plus(adder);
    +     *   dateTime = dateTime.plus(amount);
          * 
    - * It is recommended to use the second approach, {@code plus(TemporalAmount)}, + * It is recommended to use the second approach, {@code plus(amount)}, * as it is a lot clearer to read in code. * * @implSpec @@ -189,7 +189,7 @@ public interface TemporalAmount { * dateTime = amount.subtractFrom(dateTime); * dateTime = dateTime.minus(amount); * - * It is recommended to use the second approach, {@code minus(TemporalAmount)}, + * It is recommended to use the second approach, {@code minus(amount)}, * as it is a lot clearer to read in code. * * @implSpec diff --git a/src/java.base/share/classes/java/time/temporal/TemporalField.java b/src/java.base/share/classes/java/time/temporal/TemporalField.java index 54a523e64c3..4d9c620699f 100644 --- a/src/java.base/share/classes/java/time/temporal/TemporalField.java +++ b/src/java.base/share/classes/java/time/temporal/TemporalField.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 @@ -186,10 +186,10 @@ public interface TemporalField { * The second is to use {@link TemporalAccessor#isSupported(TemporalField)}: *
          *   // these two lines are equivalent, but the second approach is recommended
    -     *   temporal = thisField.isSupportedBy(temporal);
    -     *   temporal = temporal.isSupported(thisField);
    +     *   supported = thisField.isSupportedBy(temporal);
    +     *   supported = temporal.isSupported(thisField);
          * 
    - * It is recommended to use the second approach, {@code isSupported(TemporalField)}, + * It is recommended to use the second approach, {@code isSupported(thisField)}, * as it is a lot clearer to read in code. *

    * Implementations should determine whether they are supported using the fields @@ -216,10 +216,10 @@ public interface TemporalField { * The second is to use {@link TemporalAccessor#range(TemporalField)}: *

          *   // these two lines are equivalent, but the second approach is recommended
    -     *   temporal = thisField.rangeRefinedBy(temporal);
    -     *   temporal = temporal.range(thisField);
    +     *   range = thisField.rangeRefinedBy(temporal);
    +     *   range = temporal.range(thisField);
          * 
    - * It is recommended to use the second approach, {@code range(TemporalField)}, + * It is recommended to use the second approach, {@code range(thisField)}, * as it is a lot clearer to read in code. *

    * Implementations should perform any queries or calculations using the fields @@ -244,10 +244,10 @@ public interface TemporalField { * (or {@link TemporalAccessor#get(TemporalField)}): *

          *   // these two lines are equivalent, but the second approach is recommended
    -     *   temporal = thisField.getFrom(temporal);
    -     *   temporal = temporal.getLong(thisField);
    +     *   value = thisField.getFrom(temporal);
    +     *   value = temporal.getLong(thisField);
          * 
    - * It is recommended to use the second approach, {@code getLong(TemporalField)}, + * It is recommended to use the second approach, {@code getLong(thisField)}, * as it is a lot clearer to read in code. *

    * Implementations should perform any queries or calculations using the fields @@ -281,10 +281,10 @@ public interface TemporalField { * The second is to use {@link Temporal#with(TemporalField, long)}: *

          *   // these two lines are equivalent, but the second approach is recommended
    -     *   temporal = thisField.adjustInto(temporal);
    -     *   temporal = temporal.with(thisField);
    +     *   temporal = thisField.adjustInto(temporal, newValue);
    +     *   temporal = temporal.with(thisField, newValue);
          * 
    - * It is recommended to use the second approach, {@code with(TemporalField)}, + * It is recommended to use the second approach, {@code with(thisField, newValue)}, * as it is a lot clearer to read in code. *

    * Implementations should perform any queries or calculations using the fields diff --git a/src/java.base/share/classes/java/time/temporal/TemporalQueries.java b/src/java.base/share/classes/java/time/temporal/TemporalQueries.java index c0e2eaf5908..2181011e7cb 100644 --- a/src/java.base/share/classes/java/time/temporal/TemporalQueries.java +++ b/src/java.base/share/classes/java/time/temporal/TemporalQueries.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, 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 @@ -94,10 +94,10 @@ import java.time.chrono.Chronology; * The second is to use {@link TemporalAccessor#query(TemporalQuery)}: *

      *   // these two lines are equivalent, but the second approach is recommended
    - *   temporal = thisQuery.queryFrom(temporal);
    - *   temporal = temporal.query(thisQuery);
    + *   result = thisQuery.queryFrom(temporal);
    + *   result = temporal.query(thisQuery);
      * 
    - * It is recommended to use the second approach, {@code query(TemporalQuery)}, + * It is recommended to use the second approach, {@code query(thisQuery)}, * as it is a lot clearer to read in code. *

    * The most common implementations are method references, such as diff --git a/src/java.base/share/classes/java/time/temporal/TemporalQuery.java b/src/java.base/share/classes/java/time/temporal/TemporalQuery.java index 96142963062..220dec994a3 100644 --- a/src/java.base/share/classes/java/time/temporal/TemporalQuery.java +++ b/src/java.base/share/classes/java/time/temporal/TemporalQuery.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 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 @@ -81,10 +81,10 @@ import java.time.DateTimeException; * The second is to use {@link TemporalAccessor#query(TemporalQuery)}: *

      *   // these two lines are equivalent, but the second approach is recommended
    - *   temporal = thisQuery.queryFrom(temporal);
    - *   temporal = temporal.query(thisQuery);
    + *   result = thisQuery.queryFrom(temporal);
    + *   result = temporal.query(thisQuery);
      * 
    - * It is recommended to use the second approach, {@code query(TemporalQuery)}, + * It is recommended to use the second approach, {@code query(thisQuery)}, * as it is a lot clearer to read in code. *

    * The most common implementations are method references, such as @@ -115,10 +115,10 @@ public interface TemporalQuery { * The second is to use {@link TemporalAccessor#query(TemporalQuery)}: *

          *   // these two lines are equivalent, but the second approach is recommended
    -     *   temporal = thisQuery.queryFrom(temporal);
    -     *   temporal = temporal.query(thisQuery);
    +     *   result = thisQuery.queryFrom(temporal);
    +     *   result = temporal.query(thisQuery);
          * 
    - * It is recommended to use the second approach, {@code query(TemporalQuery)}, + * It is recommended to use the second approach, {@code query(thisQuery)}, * as it is a lot clearer to read in code. * * @implSpec diff --git a/src/java.base/share/classes/java/time/temporal/TemporalUnit.java b/src/java.base/share/classes/java/time/temporal/TemporalUnit.java index 9638e30c6dd..3235fe6fcee 100644 --- a/src/java.base/share/classes/java/time/temporal/TemporalUnit.java +++ b/src/java.base/share/classes/java/time/temporal/TemporalUnit.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 @@ -203,10 +203,10 @@ public interface TemporalUnit { * The second is to use {@link Temporal#plus(long, TemporalUnit)}: *
          *   // these two lines are equivalent, but the second approach is recommended
    -     *   temporal = thisUnit.addTo(temporal);
    -     *   temporal = temporal.plus(thisUnit);
    +     *   temporal = thisUnit.addTo(temporal, amount);
    +     *   temporal = temporal.plus(amount, thisUnit);
          * 
    - * It is recommended to use the second approach, {@code plus(TemporalUnit)}, + * It is recommended to use the second approach, {@code plus(amount, thisUnit)}, * as it is a lot clearer to read in code. *

    * Implementations should perform any queries or calculations using the units diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 2bab271a489..9cb1521ac62 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -79,9 +79,10 @@ import sun.util.locale.provider.TimeZoneNameUtility; * the number should be formatted according to the customs and conventions of the * user's native country, region, or culture. * - *

    The {@code Locale} class implements IETF BCP 47 which is composed of - * RFC 4647 "Matching of Language - * Tags" and RFC 5646 "Tags + *

    The {@code Locale} class implements + * IETF BCP 47 which contains + * RFC 4647 "Matching of Language + * Tags" and RFC 5646 "Tags * for Identifying Languages" with support for the LDML (UTS#35, "Unicode * Locale Data Markup Language") BCP 47-compatible extensions for locale data * exchange. Each {@code Locale} is associated with locale data which is provided @@ -101,7 +102,7 @@ import sun.util.locale.provider.TimeZoneNameUtility; *

    * {@code Locale} implements IETF BCP 47 and any deviations should be observed * by the comments prefixed by "BCP 47 deviation:". - * RFC 5646 + * RFC 5646 * combines subtags from various ISO (639, 3166, 15924) standards which are also * included in the composition of {@code Locale}. * Additionally, the full list of valid codes for each field can be found in the @@ -119,9 +120,13 @@ import sun.util.locale.provider.TimeZoneNameUtility; * {@code Locale} always canonicalizes to lower case. * *

    Syntax: Well-formed {@code language} values have the form {@code [a-zA-Z]{2,8}}.
    - *
    BCP 47 deviation: this is not the full BCP 47 language production, since it excludes - * extlang - * (as modern three-letter language codes are preferred).
    + *
    BCP 47 deviation: {@code Locale} does not retain the + * extlang + * subtag. This is because three-letter language codes are preferred over extlang + * subtags. When a {@code Locale} is created from a language tag containing an + * extlang subtag, the first extlang subtag is interpreted as the language + * field. The primary language subtag and any subsequent extlang subtags + * are ignored.
    * *
    Example: "en" (English), "ja" (Japanese), "kok" (Konkani)
    * @@ -208,7 +213,7 @@ import sun.util.locale.provider.TimeZoneNameUtility; * * * BCP 47 deviation: BCP47 defines the following two levels of - * conformance, + * conformance, * "valid" and "well-formed". A valid tag requires that it is well-formed, its * subtag values are registered in the IANA Language Subtag Registry, and it does not * contain duplicate variant or extension singleton subtags. The {@code Locale} @@ -222,8 +227,10 @@ import sun.util.locale.provider.TimeZoneNameUtility; * *

    Unicode BCP 47 U Extension

    * - *

    UTS#35, "Unicode Locale Data Markup Language" defines optional - * attributes and keywords to override or refine the default behavior + *

    UTS#35, "Unicode Locale Data Markup Language" defines the + * Unicode BCP 47 U Extension, + * an extension based on RFC 6067, + * which describes optional attributes and keywords to override or refine the default behavior * associated with a locale. A keyword is represented by a pair of * key and type. For example, "nu-thai" indicates that Thai local * digits (value:"thai") should be used for formatting numbers @@ -410,7 +417,7 @@ import sun.util.locale.provider.TimeZoneNameUtility; * with "locale" in the following locale matching documentation. * *

    In order to match a user's preferred locales to a set of language - * tags, RFC 4647 Matching of + * tags, RFC 4647 Matching of * Language Tags defines two mechanisms: filtering and lookup. * Filtering is used to get all matching locales, whereas * lookup is to select the best matching locale. @@ -516,25 +523,24 @@ import sun.util.locale.provider.TimeZoneNameUtility; *

    For compatibility reasons, two * non-conforming locales are treated as special cases. These are * {@code ja_JP_JP} and {@code th_TH_TH}. These are ill-formed - * in BCP 47 since the {@linkplain ##def_variant variants} are too short. To ease migration to BCP 47, - * these are treated specially during construction. These two cases (and only - * these) cause a constructor to generate an extension, all other values behave - * exactly as they did prior to Java 7. + * in BCP 47 since the {@linkplain ##def_variant variants} are too short. To ease + * migration to BCP 47, these are treated specially during creation. Creation + * of these two cases generates a compatibility extension. * *

    Java has used {@code ja_JP_JP} to represent Japanese as used in * Japan together with the Japanese Imperial calendar. This is now * representable using a Unicode locale extension, by specifying the * Unicode locale key {@code ca} (for "calendar") and type - * {@code japanese}. When the Locale constructor is called with the - * arguments "ja", "JP", "JP", the extension "u-ca-japanese" is - * automatically added. + * {@code japanese}. When a {@code Locale} is created with language "ja", an + * empty script, country "JP", variant "JP", and no extensions, the extension + * "u-ca-japanese" is automatically added. * *

    Java has used {@code th_TH_TH} to represent Thai as used in * Thailand together with Thai digits. This is also now representable using * a Unicode locale extension, by specifying the Unicode locale key - * {@code nu} (for "number") and value {@code thai}. When the Locale - * constructor is called with the arguments "th", "TH", "TH", the - * extension "u-nu-thai" is automatically added. + * {@code nu} (for "number") and value {@code thai}. When a {@code Locale} is + * created with language "th", an empty script, country "TH", variant "TH", and + * no extensions, the extension "u-nu-thai" is automatically added. * *

    Legacy language codes

    * @@ -546,6 +552,8 @@ import sun.util.locale.provider.TimeZoneNameUtility; * this mapping, so that resources can be named using either convention, * see {@link ResourceBundle.Control}. * + * @spec https://www.rfc-editor.org/info/bcp47 + * IETF BCP 47 * @spec https://www.rfc-editor.org/info/rfc4647 * RFC 4647: Matching of Language Tags * @spec https://www.rfc-editor.org/info/rfc5646 @@ -1603,9 +1611,9 @@ public final class Locale implements Cloneable, Serializable { *
  • Deprecated ISO language codes "iw", "ji", and "in" are * converted to "he", "yi", and "id", respectively. * - *
  • A locale with language "no", country "NO", and variant - * "NY", representing Norwegian Nynorsk (Norway), is converted - * to a language tag "nn-NO".
  • + *
  • A locale with language "no", an empty script, country "NO", variant + * "NY", and no extensions, representing Norwegian Nynorsk (Norway), is + * converted to a language tag "nn-NO".
  • * *

    Note: Although the language tag obtained by this * method is well-formed (satisfies the syntax requirements @@ -2684,6 +2692,13 @@ public final class Locale implements Cloneable, Serializable { *

  • Locale("th", "TH", "TH") is treated as "th-TH-u-nu-thai" *
  • Locale("no", "NO", "NY") is treated as "nn-NO" * + *

    For all three cases, compatibility handling only applies when the script + * is empty. Additionally, the Japanese case requires exactly the + * {@code u-ca-japanese} extension, the Thai case requires + * exactly the {@code u-nu-thai} extension, and the Norwegian case + * requires no extensions. If these conditions are not met, the two-letter + * variant is treated as ill-formed, and an {@code IllformedLocaleException} is thrown. + * * @param locale the locale * @return This builder. * @throws IllformedLocaleException if {@code locale} has @@ -2985,7 +3000,7 @@ public final class Locale implements Cloneable, Serializable { /** * This enum provides constants to select a filtering mode for locale - * matching. Refer to RFC 4647 + * matching. Refer to RFC 4647 * Matching of Language Tags for details. * *

    As an example, think of two Language Priority Lists each of which @@ -3122,7 +3137,7 @@ public final class Locale implements Cloneable, Serializable { /** * This class expresses a Language Range defined in - * RFC 4647 Matching of + * RFC 4647 Matching of * Language Tags. A language range is an identifier which is used to * select language tag(s) meeting specific requirements by using the * mechanisms described in {@linkplain Locale##LocaleMatching Locale @@ -3199,18 +3214,20 @@ public final class Locale implements Cloneable, Serializable { * * @param range a language range * @param weight a weight value between {@code MIN_WEIGHT} and - * {@code MAX_WEIGHT} + * {@code MAX_WEIGHT}, inclusive * @throws NullPointerException if the given {@code range} is * {@code null} * @throws IllegalArgumentException if the given {@code range} does not - * comply with the syntax of the language range mentioned in RFC 4647 - * or if the given {@code weight} is less than {@code MIN_WEIGHT} - * or greater than {@code MAX_WEIGHT} + * comply with the syntax of the language range mentioned in RFC 4647, + * or if the given {@code weight} is {@code Double.NaN}, less than {@code + * MIN_WEIGHT} or greater than {@code MAX_WEIGHT} */ public LanguageRange(String range, double weight) { Objects.requireNonNull(range); - if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) { - throw new IllegalArgumentException("weight=" + weight); + if (weight < MIN_WEIGHT || weight > MAX_WEIGHT || Double.isNaN(weight)) { + throw new IllegalArgumentException( + "The weight " + weight + " must be between " + + MIN_WEIGHT + " and " + MAX_WEIGHT + ", inclusive."); } range = range.toLowerCase(Locale.ROOT); @@ -3296,9 +3313,9 @@ public final class Locale implements Cloneable, Serializable { * * * In a weighted list, each language range is given a weight value. - * The weight value is identical to the "quality value" in + * The weight value has the same numeric bounds as the "quality value" * RFC 2616, and it - * expresses how much the user prefers the language. A weight value is + * expresses how much the user prefers the language. A weight value is * specified after a corresponding language range followed by * {@code ";q="}, and the default weight value is {@code MAX_WEIGHT} * when it is omitted. @@ -3341,8 +3358,9 @@ public final class Locale implements Cloneable, Serializable { * included in the given {@code ranges} and their equivalent * language ranges if available. The list is modifiable. * @throws NullPointerException if {@code ranges} is null - * @throws IllegalArgumentException if a language range or a weight - * found in the given {@code ranges} is ill-formed + * @throws IllegalArgumentException if, in the given {@code ranges}, a + * language range is ill-formed, or a weight is out of range after + * string to double conversion by {@link Double#parseDouble(String)} * @spec https://www.rfc-editor.org/info/rfc2616 RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 */ public static List parse(String ranges) { @@ -3363,8 +3381,9 @@ public final class Locale implements Cloneable, Serializable { * @return a Language Priority List with customization. The list is * modifiable. * @throws NullPointerException if {@code ranges} is null - * @throws IllegalArgumentException if a language range or a weight - * found in the given {@code ranges} is ill-formed + * @throws IllegalArgumentException if, in the given {@code ranges}, a + * language range is ill-formed, or a weight is out of range after + * string to double conversion by {@link Double#parseDouble(String)} * @spec https://www.rfc-editor.org/info/rfc2616 RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 * @see #parse(String) * @see #mapEquivalents(List, Map) diff --git a/src/java.base/share/classes/java/util/PriorityQueue.java b/src/java.base/share/classes/java/util/PriorityQueue.java index bacce5ef97e..b9ef312d66d 100644 --- a/src/java.base/share/classes/java/util/PriorityQueue.java +++ b/src/java.base/share/classes/java/util/PriorityQueue.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 @@ -209,6 +209,32 @@ public class PriorityQueue extends AbstractQueue } } + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified collection. The elements of the new {@code PriorityQueue} + * will be ordered according to the specified comparator. + * + * @param c the collection whose elements are to be placed + * into this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. + * @throws NullPointerException if the specified collection or any + * of its elements are null + * @since 28 + */ + public PriorityQueue(Collection c, + Comparator comparator) { + this.comparator = comparator; + if (c instanceof SortedSet ss && comparator == ss.comparator()) { + initElementsFromCollection(ss); + } else if (c instanceof PriorityQueue pq && comparator == pq.comparator()) { + initFromPriorityQueue(pq); + } else { + initFromCollection(c); + } + } + /** * Creates a {@code PriorityQueue} containing the elements in the * specified priority queue. This priority queue will be diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index f91db79891b..2483e184e2e 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -2842,7 +2842,7 @@ public abstract class ResourceBundle { boolean isNorwegianBokmal = false; boolean isNorwegianNynorsk = false; if (language.equals("no")) { - if (region.equals("NO") && variant.equals("NY")) { + if (region.equals("NO") && variant.equals("NY") && script.isEmpty()) { variant = ""; isNorwegianNynorsk = true; } else { diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index ba81123fc35..2711f047d4f 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -452,7 +452,8 @@ public abstract class AbstractQueuedLongSynchronizer if (q.status < 0) { // cancelled if ((s == null ? casTail(q, p) : s.casPrev(q, p)) && q.prev == p) { - p.casNext(q, s); // OK if fails + if (s != null) + p.casNext(q, s); // OK if fails if (p.prev == null) signalNext(p); } diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index c0779545083..526b459a87f 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -832,7 +832,8 @@ public abstract class AbstractQueuedSynchronizer if (q.status < 0) { // cancelled if ((s == null ? casTail(q, p) : s.casPrev(q, p)) && q.prev == p) { - p.casNext(q, s); // OK if fails + if (s != null) + p.casNext(q, s); // OK if fails if (p.prev == null) signalNext(p); } diff --git a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java index 3fbfad875d6..3dfd4793cd4 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java @@ -1450,7 +1450,8 @@ public class StampedLock implements java.io.Serializable { if (q.status < 0) { // cancelled if ((s == null ? casTail(q, p) : s.casPrev(q, p)) && q.prev == p) { - p.casNext(q, s); // OK if fails + if (s != null) + p.casNext(q, s); // OK if fails if (p.prev == null) signalNext(p); } diff --git a/src/java.base/share/classes/java/util/spi/LocaleNameProvider.java b/src/java.base/share/classes/java/util/spi/LocaleNameProvider.java index 2109a4cead1..eebfc93ac76 100644 --- a/src/java.base/share/classes/java/util/spi/LocaleNameProvider.java +++ b/src/java.base/share/classes/java/util/spi/LocaleNameProvider.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 @@ -45,7 +45,7 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider { } /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 language code and the given locale that is appropriate for * display to the user. * For example, if {@code languageCode} is "fr" and {@code locale} @@ -70,7 +70,7 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider { public abstract String getDisplayLanguage(String languageCode, Locale locale); /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 script code and the given locale that is appropriate for * display to the user. * For example, if {@code scriptCode} is "Latn" and {@code locale} @@ -100,7 +100,7 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider { } /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 region code (either ISO 3166 country code or UN M.49 area * codes) and the given locale that is appropriate for display to the user. * For example, if {@code countryCode} is "FR" and {@code locale} 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 88d08386e8c..8586dc7f63b 100644 --- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java @@ -58,6 +58,19 @@ import java.util.Objects; * The {@link #close} method should be called to release resources used by this * stream, either directly, or with the {@code try}-with-resources statement. * + * @implNote + * After reading a member trailer, the {@linkplain #read(byte[], int, int) read} method calls + * {@link InputStream#available()} on the underlying stream to determine whether additional + * bytes are available that may represent a subsequent member. If the + * {@systemProperty jdk.util.gzip.tryReadAheadAfterTrailer} system property is set + * to {@code true}, then the call to {@code InputStream.available()} is skipped and the + * implementation instead attempts to read a subsequent member in the stream. + * {@code GZIPInputStream} depends on the return value of {@code InputStream.available()} + * to reliably process a stream with a series of members. Consequently, it may be necessary + * to set this property in environments that process streams with a series of members. By default, + * the {@code jdk.util.gzip.tryReadAheadAfterTrailer} system property is not set, and + * {@code InputStream.available()} gets called. + * * @spec https://www.rfc-editor.org/info/rfc1952 * RFC 1952: GZIP file format specification version 4.3 * @@ -66,6 +79,12 @@ import java.util.Objects; * @since 1.1 */ public class GZIPInputStream extends InflaterInputStream { + + // system property which configures whether we skip the call to InputStream.available() + // when checking for additional GZIP members in a stream + private static final boolean alwaysReadNextMember = + Boolean.getBoolean("jdk.util.gzip.tryReadAheadAfterTrailer"); + /** * GZIP header magic number. */ @@ -119,7 +138,11 @@ public class GZIPInputStream extends InflaterInputStream { super(in, createInflater(in, size), size); usesDefaultInflater = true; try { - readHeader(in); + // 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); } catch (IOException ioe) { this.inf.end(); throw ioe; @@ -194,10 +217,15 @@ public class GZIPInputStream extends InflaterInputStream { } int n = super.read(buf, off, len); if (n == -1) { - if (readTrailer()) + if (hasNoMoreMembers()) { eos = true; - else + } else { + // When a next member is available, hasNoMoreMembers() will read + // its header and will position the stream at the next member's + // deflated data. We now decompress and return that member's + // decompressed data. return this.read(buf, off, len); + } } else { crc.update(buf, off, n); } @@ -221,12 +249,40 @@ 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) throws IOException { - CheckedInputStream in = new CheckedInputStream(this_in, crc); + private int readHeader(InputStream stream, boolean failOnEOF) throws IOException { + CheckedInputStream in = new CheckedInputStream(stream, 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 (readUShort(in) != GZIP_MAGIC) { + if (magic != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method @@ -268,44 +324,66 @@ public class GZIPInputStream extends InflaterInputStream { return n; } - /* - * Reads GZIP member trailer and returns true if the eos - * reached, false if there are more (concatenated gzip - * data set) + /** + * Reads the current GZIP member's trailer and returns true if the end-of-stream is + * reached. After reading the current member's trailer, if the stream has a subsequent + * GZIP member, then this method reads that member's header and returns false indicating + * that there is another member in the stream. */ - private boolean readTrailer() throws IOException { - InputStream in = this.in; - int n = inf.getRemaining(); - if (n > 0) { - in = new SequenceInputStream( - new ByteArrayInputStream(buf, len - n, n), - new FilterInputStream(in) { - public void close() throws IOException {} - }); + private boolean hasNoMoreMembers() throws IOException { + final int numRemainingInInflater = inf.getRemaining(); + InputStream stream = this.in; + if (numRemainingInInflater > 0) { + stream = new SequenceInputStream( + new ByteArrayInputStream(buf, len - numRemainingInInflater, numRemainingInInflater), + new FilterInputStream(stream) { + public void close() {} + }); } - // Uses left-to-right evaluation order - if ((readUInt(in) != crc.getValue()) || - // rfc1952; ISIZE is the input size modulo 2^32 - (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 - 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 + // first read the current member's trailer + readTrailer(stream); + // decide whether to read next member's header + final boolean readNextMember = alwaysReadNextMember + || this.in.available() > 0 + || numRemainingInInflater > 26; // current member's trailer == 8 bytes + // + minimum of 10 bytes header for next member + // + mandatory 8 bytes from next member's trailer + // == at least 26 bytes needed for next member to + // be present + if (!readNextMember) { + return true; // no need to read next member + } + // read next member's header + int m = 8; // this.trailer + try { + int numNextHeaderBytes = readHeader(stream, false); // next.header (if available) + if (numNextHeaderBytes == -1) { + return true; // end of stream reached, no more members } - inf.reset(); - if (n > m) - inf.setInput(buf, len - n + m, n - m); - return false; + m += numNextHeaderBytes; + } catch (IOException ze) { + return true; // ignore any malformed, consider it as no more members in the stream + } + inf.reset(); // reset the inflater for fresh input data from the next member + if (numRemainingInInflater > m) { + // position the inflater's input buffer to the start of next member's deflated data + inf.setInput(buf, len - numRemainingInInflater + m, numRemainingInInflater - m); + } + return false; // next member exists + } + + /** + * Reads the current member's trailer + * + * @param stream the InputStream containing the trailer + */ + private void readTrailer(final InputStream stream) throws IOException { + // Uses left-to-right evaluation order + if ((readUInt(stream) != crc.getValue()) || + // rfc1952; ISIZE is the input size modulo 2^32 + (readUInt(stream) != (inf.getBytesWritten() & 0xffffffffL))) { + throw new ZipException("Corrupt GZIP trailer"); } - return true; } /* @@ -332,14 +410,18 @@ public class GZIPInputStream extends InflaterInputStream { if (b == -1) { throw new EOFException(); } - if (b < -1 || b > 255) { - // 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); - } + 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 + throw new IOException(this.in.getClass().getName() + + ".read() returned value out of range -1..255: " + b); + } + } + /* * Skips bytes of input data blocking until all bytes are skipped. * Does not assume that the input stream is capable of seeking. diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java index 6ff5a4e00ac..27a5a075544 100644 --- a/src/java.base/share/classes/javax/crypto/Cipher.java +++ b/src/java.base/share/classes/javax/crypto/Cipher.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,6 +42,8 @@ import javax.crypto.spec.*; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; import sun.security.util.Debug; import sun.security.jca.*; import sun.security.util.KnownOIDs; @@ -515,8 +517,18 @@ public class Cipher { *

  • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  • + *
  • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  • * * @@ -541,6 +553,7 @@ public class Cipher { * * @see java.security.Provider */ + @CallerSensitive public static final Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { @@ -554,6 +567,11 @@ public class Cipher { " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("Cipher", transformation)) { + CryptoAlgorithmConstraints.warn("Cipher", transformation, + Reflection.getCallerClass()); + } + List transforms = getTransforms(transformation); List cipherServices = new ArrayList<>(transforms.size()); for (Transform transform : transforms) { @@ -623,11 +641,24 @@ public class Cipher { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param transformation the name of the transformation, * e.g., AES/CBC/PKCS5Padding. @@ -660,6 +691,7 @@ public class Cipher { * * @see java.security.Provider */ + @CallerSensitive public static final Cipher getInstance(String transformation, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, @@ -676,7 +708,7 @@ public class Cipher { throw new NoSuchProviderException("No such provider: " + provider); } - return getInstance(transformation, p); + return getInstance(transformation, p, Reflection.getCallerClass()); } private String getProviderName() { @@ -705,11 +737,24 @@ public class Cipher { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param transformation the name of the transformation, * e.g., AES/CBC/PKCS5Padding. @@ -739,6 +784,7 @@ public class Cipher { * * @see java.security.Provider */ + @CallerSensitive public static final Cipher getInstance(String transformation, Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException @@ -750,12 +796,27 @@ public class Cipher { throw new IllegalArgumentException("Missing provider"); } + return getInstance(transformation, provider, Reflection.getCallerClass()); + } + + private static Cipher getInstance(String transformation, Provider provider, + Class callerClass) + throws NoSuchAlgorithmException, NoSuchPaddingException { + if (provider == null) { + throw new IllegalArgumentException("Missing provider"); + } + // throws NoSuchAlgorithmException if java.security disables it if (!CryptoAlgorithmConstraints.permits("Cipher", transformation)) { throw new NoSuchAlgorithmException(transformation + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("Cipher", transformation)) { + CryptoAlgorithmConstraints.warn("Cipher", transformation, + callerClass); + } + Exception failure = null; List transforms = getTransforms(transformation); boolean providerChecked = false; diff --git a/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java index a2afd88c3ba..45511a8bbef 100644 --- a/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java @@ -156,12 +156,12 @@ public class DESKeySpec implements java.security.spec.KeySpec { if (key == null) { throw new NullPointerException("null key"); } - if (key.length - offset < DES_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } this.key = new byte[DES_KEY_LEN]; System.arraycopy(key, offset, this.key, 0, DES_KEY_LEN); } @@ -198,12 +198,12 @@ public class DESKeySpec implements java.security.spec.KeySpec { if (key == null) { throw new InvalidKeyException("null key"); } - if (key.length - offset < DES_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } for (int i = 0; i < DES_KEY_LEN; i++) { int k = Integer.bitCount(key[offset++] & 0xff); if ((k & 1) == 0) { @@ -235,12 +235,12 @@ public class DESKeySpec implements java.security.spec.KeySpec { if (key == null) { throw new InvalidKeyException("null key"); } - if (key.length - offset < DES_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } for (int i = 0; i < WEAK_KEYS.length; i++) { boolean found = true; for (int j = 0; j < DES_KEY_LEN; j++) { diff --git a/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java index fb5a19b4a9b..35cbbf9e462 100644 --- a/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java @@ -86,12 +86,12 @@ public class DESedeKeySpec implements java.security.spec.KeySpec { if (key == null) { throw new NullPointerException("null key"); } - if (key.length - offset < DES_EDE_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_EDE_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } this.key = new byte[24]; System.arraycopy(key, offset, this.key, 0, DES_EDE_KEY_LEN); } @@ -117,7 +117,7 @@ public class DESedeKeySpec implements java.security.spec.KeySpec { * * @exception InvalidKeyException if the given key material is * null, or starting at offset inclusive, is - * shorter than 8 bytes. + * shorter than 24 bytes. * @exception ArrayIndexOutOfBoundsException if offset is * negative. */ @@ -126,12 +126,12 @@ public class DESedeKeySpec implements java.security.spec.KeySpec { if (key == null) { throw new InvalidKeyException("null key"); } - if (key.length - offset < DES_EDE_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_EDE_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } return DESKeySpec.isParityAdjusted(key, offset) && DESKeySpec.isParityAdjusted(key, offset + 8) && DESKeySpec.isParityAdjusted(key, offset + 16); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 71ce907efc4..3fafc9fdc8b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.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. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1110,6 +1110,9 @@ public final class StackMapGenerator { locals[localsSize++] = type; } } + if (locals != null && localsSize < locals.length) { + Arrays.fill(locals, localsSize, locals.length, Type.TOP_TYPE); + } this.localsSize = localsSize; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 6411c939549..d19dd202432 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.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 @@ -189,16 +189,18 @@ public final class Util { public static List entryList(List list) { var result = new Object[list.size()]; // null check - for (int i = 0; i < result.length; i++) { - result[i] = TemporaryConstantPool.INSTANCE.classEntry(list.get(i)); + int i = 0; + for (var entry : list) { + result[i++] = TemporaryConstantPool.INSTANCE.classEntry(entry); } return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result); } public static List moduleEntryList(List list) { var result = new Object[list.size()]; // null check - for (int i = 0; i < result.length; i++) { - result[i] = TemporaryConstantPool.INSTANCE.moduleEntry(TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i).name())); + int i = 0; + for (var entry : list) { + result[i++] = TemporaryConstantPool.INSTANCE.moduleEntry(entry); } return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result); } diff --git a/src/java.base/share/classes/jdk/internal/event/EventHelper.java b/src/java.base/share/classes/jdk/internal/event/EventHelper.java index 4da2d5854cb..2ad233e624f 100644 --- a/src/java.base/share/classes/jdk/internal/event/EventHelper.java +++ b/src/java.base/share/classes/jdk/internal/event/EventHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -67,12 +67,13 @@ public final class EventHelper { int peerPort, String cipherSuite, String protocolVersion, + String namedGroup, long peerCertId) { assert securityLogger != null; String prepend = getDurationString(start); securityLogger.log(LOG_LEVEL, prepend + - " TLSHandshake: {0}:{1,number,#}, {2}, {3}, {4,number,#}", - peerHost, peerPort, protocolVersion, cipherSuite, peerCertId); + " TLSHandshake: {0}:{1,number,#}, {2}, {3}, {4}, {5,number,#}", + peerHost, peerPort, protocolVersion, cipherSuite, namedGroup, peerCertId); } public static void logSecurityPropertyEvent(String key, diff --git a/src/java.base/share/classes/jdk/internal/event/TLSHandshakeEvent.java b/src/java.base/share/classes/jdk/internal/event/TLSHandshakeEvent.java index f763a0e4d29..c1d3c5bf404 100644 --- a/src/java.base/share/classes/jdk/internal/event/TLSHandshakeEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/TLSHandshakeEvent.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 @@ -34,5 +34,6 @@ public final class TLSHandshakeEvent extends Event { public int peerPort; public String protocolVersion; public String cipherSuite; + public String namedGroup; public long certificateId; } diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 064e4e1fd92..6d63ae24f85 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -57,6 +57,11 @@ public @interface PreviewFeature { */ public Feature feature(); + /** + * A reflective preview API may be used without causing a compilation error + * when preview features are disabled (JLS {@jls 1.5.1}), but still causes + * a preview warning. + */ public boolean reflective() default false; /** @@ -71,7 +76,14 @@ public @interface PreviewFeature { @JEP(number=538, title="PEM Encodings of Cryptographic Objects", status="Third Preview") PEM_API, - LANGUAGE_MODEL, + /** + * Indicates a preview API exists to allow access to the environment + * where all preview features of the current Java SE release are enabled. + * Such an API is usually {@link #reflective()} and never intended to + * become permanent. This "feature" does not have a JEP and its APIs + * are not displayed in the "Preview API" page in Javadoc output. + */ + PREVIEW_SUPPORT, /** * A key for testing. */ diff --git a/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java index 20d3b8837fe..59cb8db1d3e 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java @@ -75,9 +75,10 @@ public class BasicImageReader implements AutoCloseable { private final ByteOrder byteOrder; private final String name; private final ByteBuffer memoryMap; + private final boolean isMemoryMapped; private final FileChannel channel; private final ImageHeader header; - private final long indexSize; + private final int indexSize; private final IntBuffer redirect; private final IntBuffer offsets; private final ByteBuffer locations; @@ -92,14 +93,15 @@ public class BasicImageReader implements AutoCloseable { this.byteOrder = Objects.requireNonNull(byteOrder); this.name = this.imagePath.toString(); - ByteBuffer map; + // The image reader will be for the current run-time image when the this class + // is defined by the boot class loader. + final boolean isCurrentRuntimeImage = BasicImageReader.class.getClassLoader() == null; + ByteBuffer map = null; - if (USE_JVM_MAP && BasicImageReader.class.getClassLoader() == null) { + if (USE_JVM_MAP && isCurrentRuntimeImage) { // Check to see if the jvm has opened the file using libjimage // native entry when loading the image for this runtime map = NativeImageBuffer.getNativeMap(name); - } else { - map = null; } // Open the file only if no memory map yet or is 32 bit jvm @@ -111,7 +113,7 @@ public class BasicImageReader implements AutoCloseable { AccessController.doPrivileged(new PrivilegedAction() { @Override public Void run() { - if (BasicImageReader.class.getClassLoader() == null) { + if (isCurrentRuntimeImage) { try { Class fileChannelImpl = Class.forName("sun.nio.ch.FileChannelImpl"); @@ -132,23 +134,21 @@ public class BasicImageReader implements AutoCloseable { }); } - // If no memory map yet and 64 bit jvm then memory map entire file - if (MAP_ALL && map == null) { + isMemoryMapped = isCurrentRuntimeImage && MAP_ALL; + + // If no memory map yet, runtime image, and 64 bit jvm then memory map entire file + if (map == null && isMemoryMapped) { map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); } + // Assume we have a memory map to read image file header ByteBuffer headerBuffer = map; int headerSize = ImageHeader.getHeaderSize(); // If no memory map then read header from image file if (headerBuffer == null) { - headerBuffer = ByteBuffer.allocateDirect(headerSize); - if (channel.read(headerBuffer, 0L) == headerSize) { - headerBuffer.rewind(); - } else { - throw new IOException("\"" + name + "\" is not an image file"); - } + headerBuffer = readDirectBuffer(headerSize); } else if (headerBuffer.capacity() < headerSize) { throw new IOException("\"" + name + "\" is not an image file"); } @@ -157,10 +157,9 @@ public class BasicImageReader implements AutoCloseable { header = readHeader(intBuffer(headerBuffer, 0, headerSize)); indexSize = header.getIndexSize(); - // If no memory map yet then must be 32 bit jvm not previously mapped + // If no memory map yet then must be 32 bit jvm or not runtime image not previously mapped if (map == null) { - // Just map the image index - map = channel.map(FileChannel.MapMode.READ_ONLY, 0, indexSize); + map = readDirectBuffer(indexSize); } memoryMap = map.asReadOnlyBuffer(); @@ -178,6 +177,16 @@ public class BasicImageReader implements AutoCloseable { decompressor = new Decompressor(); } + private ByteBuffer readDirectBuffer(int size) throws IOException { + ByteBuffer buffer = ByteBuffer.allocateDirect(size); + if (channel.read(buffer, 0L) == size) { + buffer.flip(); + } else { + throw new IOException("\"" + name + "\" is not an image file"); + } + return buffer; + } + protected BasicImageReader(Path imagePath) throws IOException { this(imagePath, ByteOrder.nativeOrder()); } @@ -413,7 +422,7 @@ public class BasicImageReader implements AutoCloseable { } int checkedSize = (int) size; - if (MAP_ALL) { + if (isMemoryMapped) { ByteBuffer buffer = slice(memoryMap, checkedOffset, checkedSize); buffer.order(ByteOrder.BIG_ENDIAN); diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java index 2cf28b835ce..59c2392dea9 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java @@ -486,7 +486,7 @@ public final class ImageReader implements AutoCloseable { ImageLocation loc = null; if (isPreviewEnabled) { // We must test preview location first (if in preview mode). - loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + resourcePath); + loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + "/" + resourcePath); } if (loc == null) { loc = findLocation(moduleName, resourcePath); @@ -531,7 +531,7 @@ public final class ImageReader implements AutoCloseable { return node.isResource(); } } - loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + resourcePath); + loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + "/" + resourcePath); } if (loc == null) { loc = findLocation(moduleName, resourcePath); @@ -561,7 +561,19 @@ public final class ImageReader implements AutoCloseable { // Now try the non-prefixed resource name, but be careful to avoid false // positives for names like "/modules/modules/xxx" which could return a // location of a directory entry. - loc = findLocation(name.substring(MODULES_PREFIX.length())); + String resourceName = name.substring(MODULES_PREFIX.length()); + if (isPreviewEnabled) { + // Root-level preview resources are not pre-cached when an image + // is opened, so check for them first. + int pathStart = resourceName.indexOf('/', 1); + if (pathStart > 1 && resourceName.indexOf('/', pathStart + 1) < 0) { + loc = findLocation(resourceName.substring(0, pathStart) + + PREVIEW_INFIX + "/" + resourceName.substring(pathStart + 1)); + } + } + if (loc == null) { + loc = findLocation(resourceName); + } return loc != null && loc.getType() == RESOURCE ? ensureCached(newResource(name, loc)) : null; @@ -649,6 +661,36 @@ public final class ImageReader implements AutoCloseable { private Directory completeModuleDirectory(Directory dir, ImageLocation loc) { assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; List previewOnlyNodes = getPreviewNodesToMerge(dir); + if (isPreviewEnabled && previewOnlyNodes.isEmpty()) { + // When opening an image in preview mode, packages that have preview + // content are eagerly processed, caching preview resources and + // preview-only directories for direct lookup. Root-level preview + // resources are omitted during this process, since they have no + // package path and the empty package is not represented under + // "/packages", and must be processed separately. + int moduleStart = MODULES_PREFIX.length() + 1; + if (dir.getName().indexOf('/', moduleStart) < 0) { + ImageLocation previewLoc = findLocation(dir.getName() + PREVIEW_INFIX); + if (previewLoc != null) { + previewOnlyNodes = createChildNodes(previewLoc, 0, childLoc -> { + String baseName = getBaseName(childLoc); + String nonPreviewChildName = dir.getName() + "/" + baseName; + boolean isPreviewOnly = ImageLocation.isPreviewOnly(childLoc.getFlags()); + LocationType type = childLoc.getType(); + if (type == RESOURCE) { + Node childNode = nodes.computeIfAbsent(nonPreviewChildName, n -> newResource(n, childLoc)); + return isPreviewOnly ? childNode : null; + } else { + assert type == MODULES_DIR : "Invalid location type: " + childLoc; + Node childNode = nodes.get(nonPreviewChildName); + assert !(isPreviewOnly && childNode == null) : + "Inconsistent child node: " + nonPreviewChildName; + return isPreviewOnly ? childNode : null; + } + }); + } + } + } // We hide preview names from direct lookup, but must also prevent // the preview directory from appearing in any META-INF directories. boolean parentIsMetaInfDir = isMetaInf(dir); diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java index 016566ae659..dd4b9e079c4 100644 --- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -1783,10 +1783,7 @@ public final class Unsafe { *

    The JNI documents specify that, at least for returning * values from native methods, a Java boolean value is converted * to the value-set 0..1 by first truncating to a byte (0..255 or - * maybe -128..127) and then testing against zero. Thus, Java - * booleans in non-Java data structures are by convention - * represented as 8-bit containers containing either zero (for - * false) or any non-zero value (for true). + * maybe -128..127) and then testing against zero. * *

    Java booleans in the heap are also stored in bytes, but are * strongly normalized to the value-set 0..1 (i.e., they are @@ -1797,17 +1794,17 @@ public final class Unsafe { * bit can be usually implemented with fewer (machine) * instructions than byte testing against zero. * - *

    A number of Unsafe methods load boolean values from the heap - * as bytes. Unsafe converts those values according to the JNI - * rules (i.e, using the "testing against zero" convention). The - * method {@code byte2bool} implements that conversion. + *

    A number of Unsafe methods load boolean values as bytes, + * truncate them to the least-significant bit, and then test + * against zero. It is uniformly performed for both Java heap + * and non-Java heap accesses. * * @param b the byte to be converted to boolean * @return the result of the conversion */ @ForceInline private boolean byte2bool(byte b) { - return b != 0; + return (b & 1) != 0; } /** diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java index c3a0c283dc3..05f582b9a54 100644 --- a/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.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 @@ -64,10 +64,7 @@ import java.lang.annotation.Target; /// an AOT-initialized class, at the end of the assembly phase run which builds /// an AOT cache. The `resetArchivedStates` may "tear down" state that should /// not be stored in the AOT cache, which the `runtimeSetup` method may then -/// "build up again" as the production run begins. This additional method is -/// currently only used by [Class] to reset a cache field, but it may be -/// expanded to other classes and interfaces later on, using more -/// annotation-driven logic. +/// "build up again" as the production run begins. /// /// The logic in `classFileParser.cpp` performs checks on the annotated method: If the /// annotated method's signature differs from that described above, or if (during the diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java index a94f58159a2..735e3447b37 100644 --- a/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java @@ -35,7 +35,7 @@ import java.lang.annotation.Target; /// /// The compiler already treats static final fields and instance final fields in /// record classes and hidden classes as constant. All classes in select -/// packages (Defined in `trust_final_non_static_fields` in `ciField.cpp`) in +/// packages (Defined in `trust_final_nonstatic_fields` in `ciField.cpp`) in /// the boot class loader also have their instance final fields trusted. This /// annotation is not necessary in these cases. /// diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index 23a787971c0..07da636e781 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java @@ -156,7 +156,8 @@ public class VectorSupport { LT_BYTE = 2, LT_SHORT = 3, LT_INT = 4, - LT_LONG = 5; + LT_LONG = 5, + LT_FLOAT16 = 6; /* ============================================================================ */ diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties b/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties index e80869b868c..f01d079da64 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,8 @@ java.launcher.opt.footer = \ -cp ...|:]\n -enableassertions[:...|:]\n Aktiviert Assertions mit angegebener Granularität\n -da[:...|:]\n -disableassertions[:...|:]\n Deaktiviert Assertions mit angegebener Granularität\n -esa | -enablesystemassertions\n Aktiviert System-Assertions\n -dsa | -disablesystemassertions\n Deaktiviert System-Assertions\n -agentlib:[=]\n Lädt die native Agent Library . Beispiel: -agentlib:jdwp\n siehe auch -agentlib:jdwp=help\n -agentpath:[=]\n Lädt die native Agent Library mit dem vollständigen Pfadnamen\n -javaagent:[=]\n Lädt den Java-Programmiersprachen-Agent, siehe java.lang.instrument\n -splash:\n Zeigt den Startbildschirm mit einem angegebenen Bild an\n Skalierte HiDPI-Bilder werden automatisch unterstützt und verwendet,\n falls verfügbar. Der nicht skalierte Bilddateiname (Beispiel: image.ext)\n muss immer als Argument an die Option "-splash" übergeben werden.\n Das am besten geeignete angegebene skalierte Bild wird\n automatisch ausgewählt.\n Weitere Informationen finden Sie in der Dokumentation zur SplashScreen-API\n @argument files\n Eine oder mehrere Argumentdateien mit Optionen\n --disable-@files\n Verhindert die weitere Erweiterung von Argumentdateien\n --enable-preview\n Lässt zu, das Klassen von Vorschaufeatures dieses Release abhängig sind\nUm ein Argument für eine lange Option anzugeben, können Sie --= oder\n-- verwenden.\n # Translators please note do not translate the options themselves -java.launcher.X.usage=\n -Xbatch Deaktiviert die Hintergrundkompilierung\n -Xbootclasspath/a:\n An das Ende des Bootstrap Classpaths anhängen\n -Xcheck:jni Führt zusätzliche Prüfungen für JNI-Funktionen aus\n -Xcomp Erzwingt die Kompilierung von Methoden beim ersten Aufruf\n -Xdebug Führt keine Aktion aus. Ist veraltet und wird in einem zukünftigen Release entfernt.\n -Xdiag Zeigt zusätzliche Diagnosemeldungen an\n -Xint Nur Ausführung im interpretierten Modus\n -Xinternalversion\n Zeigt detailliertere JVM-Versionsinformationen an als die\n Option -version\n -Xlog: Konfiguriert oder aktiviert Logging mit dem einheitlichen Java Virtual\n Machine-(JVM-)Logging-Framework. Verwenden Sie -Xlog:help\n für weitere Einzelheiten.\n -Xloggc: Protokolliert den GC-Status in einer Datei mit Zeitstempeln.\n Diese Option ist veraltet und kann in einem\n zukünftigen Release entfernt werden. Wird durch -Xlog:gc: ersetzt.\n -Xmixed Ausführung im gemischten Modus (Standard)\n -Xmn Legt die anfängliche und maximale Größe (in Byte) des Heaps\n für die Young Generation (Nursery) fest\n -Xms Legt die minimale und die anfängliche Java-Heap-Größe fest\n -Xmx Legt die maximale Java-Heap-Größe fest\n -Xnoclassgc Deaktiviert die Klassen-Garbage Collection\n -Xrs Reduziert die Verwendung von BS-Signalen durch Java/VM (siehe Dokumentation)\n -Xshare:auto Verwendet freigegebene Klassendaten, wenn möglich (Standard)\n -Xshare:off Versucht nicht, freigegebene Klassendaten zu verwenden\n -Xshare:on Erfordert die Verwendung freigegebener Klassendaten, verläuft sonst nicht erfolgreich.\n Diese Testoption kann zeitweise zu\n Fehlern führen. Sie darf nicht in Produktionsumgebungen verwendet werden.\n -XshowSettings Zeigt alle Einstellungen an und fährt fort\n -XshowSettings:all\n Zeigt alle Einstellungen als Verbose-Ausgabe an und fährt fort\n -XshowSettings:locale\n Zeigt alle gebietsschemabezogenen Einstellungen an und fährt fort\n -XshowSettings:properties\n Zeigt alle Eigenschaftseinstellungen an und fährt fort\n -XshowSettings:vm\n Zeigt alle VM-bezogenen Einstellungen an und fährt fort\n -XshowSettings:security\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:all\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:properties\n Zeigt Sicherheitseigenschaften an und fährt fort\n -XshowSettings:security:providers\n Zeigt statische Sicherheitsprovidereinstellungen an und fährt fort\n -XshowSettings:security:tls\n Zeigt TLS-bezogene Sicherheitseinstellungen an und fährt fort\n -XshowSettings:system\n (Nur Linux) Zeigt die Konfiguration des Hostsystems oder Containers an\n und fährt fort\n -Xss Legt die Stackgröße des Java-Threads fest\n Die tatsächliche Größe kann auf ein Vielfaches der\n Systemseitengröße aufgerundet werden, wenn für das Betriebssystem erforderlich.\n -Xverify Legt den Modus der Bytecodeverifizierung fest\n \ - Beachten Sie, dass die Option -Xverify:none veraltet ist und\n in einem zukünftigen Release entfernt werden kann.\n --add-reads =(,)*\n Aktualisiert , damit gelesen wird, ungeachtet\n der Moduldeklaration. \n kann ALL-UNNAMED sein, um alle unbenannten\n Module zu lesen.\n --add-exports /=(,)*\n Aktualisiert , um in zu exportieren,\n ungeachtet der Moduldeklaration.\n kann ALL-UNNAMED sein, um in alle\n unbenannten Module zu exportieren.\n --add-opens /=(,)*\n Aktualisiert , um in\n zu öffnen, ungeachtet der Moduldeklaration.\n --limit-modules [,...]\n Grenzt die Gesamtmenge der beobachtbaren Module ein\n --patch-module =({0})*\n Überschreibt oder erweitert ein Modul mit Klassen und Ressourcen\n in JAR-Dateien oder Verzeichnissen.\n --source \n Legt die Version der Quelle im Quelldateimodus fest.\n --finalization=\n Steuert, ob die JVM Objekte finalisiert.\n Dabei ist entweder "enabled" oder "disabled".\n Die Finalisierung ist standardmäßig aktiviert.\n --sun-misc-unsafe-memory-access=\n Verwendung der nicht unterstützten API sun.misc.Unsafe zulassen oder verweigern\n ist "allow", "warn", "debug" oder "deny".\n Der Standardwert ist "warn".\n\nDiese zusätzlichen Optionen können jederzeit ohne vorherige Ankündigung geändert werden.\n +java.launcher.X.usage=\n -Xbatch Deaktiviert die Hintergrundkompilierung\n -Xbootclasspath/a:\n An das Ende des Bootstrap Classpaths anhängen\n -Xcheck:jni Führt zusätzliche Prüfungen für JNI-Funktionen aus\n -Xcomp Erzwingt die Kompilierung von Methoden beim ersten Aufruf\n -Xdebug Führt keine Aktion aus. Ist veraltet und wird in einem zukünftigen Release entfernt.\n -Xdiag Zeigt zusätzliche Diagnosemeldungen an\n -Xint Nur Ausführung im interpretierten Modus\n -Xinternalversion\n Zeigt detailliertere JVM-Versionsinformationen an als die\n Option -version\n -Xlog: Konfiguriert oder aktiviert Logging mit dem einheitlichen Java Virtual\n Machine-(JVM-)Logging-Framework. Verwenden Sie -Xlog:help\n für weitere Einzelheiten.\n -Xloggc: Protokolliert den GC-Status in einer Datei mit Zeitstempeln.\n Diese Option ist veraltet und kann in einem\n zukünftigen Release entfernt werden. Wird durch -Xlog:gc: ersetzt.\n -Xmixed Ausführung im gemischten Modus (Standard)\n -Xmn Legt die anfängliche und maximale Größe (in Byte) des Heaps\n für die Young Generation (Nursery) fest\n -Xms Legt die minimale und die anfängliche Java-Heap-Größe fest\n -Xmx Legt die maximale Java-Heap-Größe fest\n -Xnoclassgc Deaktiviert die Klassen-Garbage Collection\n -Xrs Reduziert die Verwendung von BS-Signalen durch Java/VM (siehe Dokumentation)\n -Xshare:auto Verwendet freigegebene Klassendaten, wenn möglich (Standard)\n -Xshare:off Versucht nicht, freigegebene Klassendaten zu verwenden\n -Xshare:on Erfordert die Verwendung freigegebener Klassendaten, verläuft sonst nicht erfolgreich.\n Diese Testoption kann zeitweise zu\n Fehlern führen. Sie darf nicht in Produktionsumgebungen verwendet werden.\n -XshowSettings Zeigt alle Einstellungen an und fährt fort\n -XshowSettings:all\n Zeigt alle Einstellungen als Verbose-Ausgabe an und fährt fort\n -XshowSettings:locale\n Zeigt alle gebietsschemabezogenen Einstellungen an und fährt fort\n -XshowSettings:properties\n Zeigt alle Eigenschaftseinstellungen an und fährt fort\n -XshowSettings:vm\n Zeigt alle VM-bezogenen Einstellungen an und fährt fort\n -XshowSettings:security\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:all\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:properties\n Zeigt Sicherheitseigenschaften an und fährt fort\n -XshowSettings:security:providers\n Zeigt statische Sicherheitsprovidereinstellungen an und fährt fort\n -XshowSettings:security:tls\n Zeigt TLS-bezogene Sicherheitseinstellungen an und fährt fort\n -XshowSettings:system\n (Nur Linux) Zeigt die Konfiguration des Hostsystems oder Containers an\n und fährt fort\n -Xss Legt die Stackgröße des Java-Threads fest\n Die tatsächliche Größe kann auf ein Vielfaches der\n Systemseitengröße aufgerundet werden, wenn für das Betriebssystem erforderlich.\n -Xverify Legt den Modus der Bytecodeverifizierung fest\n \ +--add-reads =(,)*\n Aktualisiert , damit gelesen wird, ungeachtet\n der Moduldeklaration. \n kann ALL-UNNAMED sein, um alle unbenannten\n Module zu lesen.\n --add-exports /=(,)*\n Aktualisiert , um in zu exportieren,\n ungeachtet der Moduldeklaration.\n kann ALL-UNNAMED sein, um in alle\n unbenannten Module zu exportieren.\n --add-opens /=(,)*\n Aktualisiert , um in\n zu öffnen, ungeachtet der Moduldeklaration.\n --limit-modules [,...]\n Grenzt die Gesamtmenge der beobachtbaren Module ein\n --patch-module =({0})*\n Überschreibt oder erweitert ein Modul mit Klassen und Ressourcen\n in JAR-Dateien oder Verzeichnissen.\n --source \n Legt die Version der Quelle im Quelldateimodus fest.\n --finalization=\n Steuert, ob die JVM Objekte finalisiert.\n Dabei ist entweder "enabled" oder "disabled".\n Die Finalisierung ist standardmäßig aktiviert.\n --sun-misc-unsafe-memory-access=\n Verwendung der nicht unterstützten API sun.misc.Unsafe zulassen oder verweigern\n ist "allow", "warn", "debug" oder "deny".\n Der Standardwert ist "warn".\n\nDiese zusätzlichen Optionen können jederzeit ohne vorherige Ankündigung geändert werden.\n # Translators please note do not translate the options themselves java.launcher.X.macosx.usage=\nDie folgenden Optionen sind für macOS spezifisch:\n -XstartOnFirstThread\n Führt die main()-Methode für den ersten (AppKit-)Thread aus\n -Xdock:name=\n Setzt den im Dock angezeigten Standardanwendungsnamen außer Kraft\n -Xdock:icon=\n Setzt das im Dock angezeigte Standardsymbol außer Kraft\n\n diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties b/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties index 49712b21c52..e03c385b97e 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. # 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,8 +37,8 @@ java.launcher.opt.footer = \ -cp <ディレクトリおよびzip/jarファイ # Translators please note do not translate the options themselves java.launcher.X.usage=\n -Xbatch バックグラウンド・コンパイルを無効にします\n -Xbootclasspath/a:\n ブートストラップ・クラス・パスの最後に追加します\n -Xcheck:jni JNI関数に対する追加のチェックを実行します\n -Xcomp 初回呼出し時にメソッドのコンパイルを強制します\n -Xdebug 何も実行されません。将来のリリースで削除されるため、非推奨になりました。\n -Xdiag 追加の診断メッセージを表示します\n -Xint インタプリタ・モードの実行のみ\n -Xinternalversion\n -versionオプションより詳細なJVMバージョン情報を\n 表示します\n -Xlog: Java Virtual Machine (JVM)統合ロギング・フレームワークでの\n ロギングを構成または有効化します。詳細は、-Xlog:helpを\n 使用してください。\n -Xloggc: タイムスタンプが付いたファイルにGCステータスのログを記録します\n このオプションは非推奨であり、将来のリリースで削除される\n 可能性があります。-Xlog:gc:で置換されています。\n -Xmixed 混合モードの実行(デフォルト)\n -Xmn 若い世代(ナーサリ)のヒープの初期サイズおよび最大サイズ\n (バイト単位)を設定します\n -Xms Javaの最小および初期のヒープ・サイズを設定します\n -Xmx Javaの最大ヒープ・サイズを設定します\n -Xnoclassgc クラスのガベージ・コレクションを無効にします\n -Xrs Java/VMによるOSシグナルの使用を削減します(ドキュメントを参照)\n -Xshare:auto 可能であれば共有クラス・データを使用します(デフォルト)\n -Xshare:off 共有クラス・データの使用を試みません\n -Xshare:on 共有クラス・データの使用を必須にし、できなければ失敗します。\n \ -これはテスト・オプションであり、断続的な失敗につながる\n 可能性があります。本番環境では使用しないでください。\n -XshowSettings すべての設定を表示して続行します\n -XshowSettings:all\n すべての設定を詳細に表示して続行します\n -XshowSettings:locale\n すべてのロケール関連の設定を表示して続行します\n -XshowSettings:properties\n すべてのプロパティ設定を表示して続行します\n -XshowSettings:vm\n すべてのVM関連の設定を表示して続行します\n -XshowSettings:security\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:all\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:properties\n セキュリティ・プロパティを表示して続行します\n -XshowSettings:security:providers\n 静的セキュリティ・プロバイダ設定を表示して続行します\n -XshowSettings:security:tls\n TLS関連のセキュリティ設定を表示して続行します\n -XshowSettings:system\n (Linuxのみ)ホスト・システムまたはコンテナを表示します\n 構成して続行します\n -Xss javaスレッドのスタック・サイズを設定します\n 実際のサイズは、次の倍数に切り上げられる場合があります: \n オペレーティング・システムの要件に応じたシステム・ページ・サイズ。\n -Xverify バイトコード・ベリファイアのモードを設定します\n オプション-Xverify:noneは非推奨になり、\n 将来のリリースで削除される可能性があります。\n --add-reads =(,)*\n モジュール宣言に関係なく、を更新してを\n 読み取ります。 \n をALL-UNNAMEDに設定すると、すべての名前のないモジュールを\n 読み取ることができます。\n --add-exports \ -/=(,)*\n モジュール宣言に関係なく、を更新してに\n エクスポートします。\n をALL-UNNAMEDに設定すると、すべての名前のないモジュールに\n エクスポートできます。\n --add-opens /=(,)*\n モジュール宣言に関係なく、を更新してを\n に開きます。\n --limit-modules [,...]\n 参照可能なモジュールの領域を制限します\n --patch-module =({0})*\n JARファイルまたはディレクトリのクラスおよびリソースで\n モジュールをオーバーライドまたは拡張します。\n --source \n ソースファイル・モードでソースのバージョンを設定します。\n --finalization=\n JVMがオブジェクトのファイナライズを実行するかどうかを制御します\n は"enabled"または"disabled"のいずれかです。\n ファイナライズはデフォルトで有効になっています。\n --sun-misc-unsafe-memory-access=\n サポートされていないAPI sun.misc.Unsafeの使用を許可または拒否します\n は"allow"、"warn"、"debug"または"deny"のいずれかです。\n デフォルト値は"warn"です。\n\nこの追加オプションは予告なしに変更されることがあります。\n +これはテスト・オプションであり、断続的な失敗につながる\n 可能性があります。本番環境では使用しないでください。\n -XshowSettings すべての設定を表示して続行します\n -XshowSettings:all\n すべての設定を詳細に表示して続行します\n -XshowSettings:locale\n すべてのロケール関連の設定を表示して続行します\n -XshowSettings:properties\n すべてのプロパティ設定を表示して続行します\n -XshowSettings:vm\n すべてのVM関連の設定を表示して続行します\n -XshowSettings:security\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:all\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:properties\n セキュリティ・プロパティを表示して続行します\n -XshowSettings:security:providers\n 静的セキュリティ・プロバイダ設定を表示して続行します\n -XshowSettings:security:tls\n TLS関連のセキュリティ設定を表示して続行します\n -XshowSettings:system\n (Linuxのみ)ホスト・システムまたはコンテナを表示します\n 構成して続行します\n -Xss javaスレッドのスタック・サイズを設定します\n 実際のサイズは、次の倍数に切り上げられる場合があります: \n オペレーティング・システムの要件に応じたシステム・ページ・サイズ。\n -Xverify バイトコード・ベリファイアのモードを設定します\n --add-reads =(,)*\n モジュール宣言に関係なく、を更新してを\n 読み取ります。 \n をALL-UNNAMEDに設定すると、すべての名前のないモジュールを\n 読み取ることができます。\n --add-exports /=(,)*\n モジュール宣言に関係なく、を更新してに\n \ +エクスポートします。\n をALL-UNNAMEDに設定すると、すべての名前のないモジュールに\n エクスポートできます。\n --add-opens /=(,)*\n モジュール宣言に関係なく、を更新してを\n に開きます。\n --limit-modules [,...]\n 参照可能なモジュールの領域を制限します\n --patch-module =({0})*\n JARファイルまたはディレクトリのクラスおよびリソースで\n モジュールをオーバーライドまたは拡張します。\n --source \n ソースファイル・モードでソースのバージョンを設定します。\n --finalization=\n JVMがオブジェクトのファイナライズを実行するかどうかを制御します\n は"enabled"または"disabled"のいずれかです。\n ファイナライズはデフォルトで有効になっています。\n --sun-misc-unsafe-memory-access=\n サポートされていないAPI sun.misc.Unsafeの使用を許可または拒否します\n は"allow"、"warn"、"debug"または"deny"のいずれかです。\n デフォルト値は"warn"です。\n\nこの追加オプションは予告なしに変更されることがあります。\n # Translators please note do not translate the options themselves java.launcher.X.macosx.usage=\n次のオプションはmacOS固有です:\n -XstartOnFirstThread\n main()メソッドを最初(AppKit)のスレッドで実行する\n -Xdock:name=\n Dockに表示されるデフォルト・アプリケーション名をオーバーライドする\n -Xdock:icon=\n Dockに表示されるデフォルト・アイコンをオーバーライドする\n\n diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties b/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties index b3c0268f953..734fc9b50f7 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. # 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 @@ java.launcher.opt.footer = \ -cp <目录和 zip/jar 文件的类搜索路径> # Translators please note do not translate the options themselves java.launcher.X.usage=\n -Xbatch 禁用后台编译\n -Xbootclasspath/a:<以 {0} 分隔的目录和 zip/jar 文件>\n 附加在引导类路径末尾\n -Xcheck:jni 对 JNI 函数执行其他检查\n -Xcomp 强制在首次调用时编译方法\n -Xdebug 不执行任何操作;已过时,将在未来发行版中删除。\n -Xdiag 显示附加诊断消息\n -Xint 仅解释模式执行\n -Xinternalversion\n 显示比 -version 选项更详细的\n JVM 版本信息\n -Xlog: 配置或启用采用 Java 虚拟\n 机 (Java Virtual Machine, JVM) 统一记录框架进行事件记录。使用 -Xlog:help\n 可了解详细信息。\n -Xloggc: 将 GC 状态记录在文件中(带时间戳)。\n 此选项已过时,可能会在\n 将来的发行版中删除。它将替换为 -Xlog:gc:。\n -Xmixed 混合模式执行(默认值)\n -Xmn 为年轻代(新生代)设置初始和最大堆大小\n (以字节为单位)\n -Xms 设置最小和初始 Java 堆大小\n -Xmx 设置最大 Java 堆大小\n -Xnoclassgc 禁用类垃圾收集\n -Xrs 减少 Java/VM 对操作系统信号的使用(请参见文档)\n -Xshare:auto 在可能的情况下使用共享类数据(默认值)\n -Xshare:off 不尝试使用共享类数据\n -Xshare:on 要求使用共享类数据,否则将失败。\n 这是一个测试选项,可能导致间歇性\n 故障。不应在生产环境中使用它。\n -XshowSettings 显示所有设置并继续\n -XshowSettings:all\n 详细显示所有设置并继续\n -XshowSettings:locale\n 显示所有与区域设置相关的设置并继续\n -XshowSettings:properties\n 显示所有属性设置并继续\n -XshowSettings:vm\n 显示所有与 vm 相关的设置并继续\n -XshowSettings:security\n 显示所有安全设置并继续\n -XshowSettings:security:all\n 显示所有安全设置并继续\n -XshowSettings:security:properties\n 显示安全属性并继续\n -XshowSettings:security:providers\n 显示静态安全提供方设置并继续\n -XshowSettings:security:tls\n 显示与 TLS \ -相关的安全设置并继续\n -XshowSettings:system\n (仅 Linux)显示主机系统或容器\n 配置并继续\n -Xss 设置 Java 线程堆栈大小\n 实际大小可以舍入到\n 操作系统要求的系统页面大小的倍数。\n -Xverify 设置字节码验证器的模式\n 请注意,选项 -Xverify:none 已过时,\n 可能会在未来发行版中删除。\n --add-reads =(,)*\n 更新 以读取 ,而无论\n 模块如何声明。 \n 可以是 ALL-UNNAMED,将读取所有未命名\n 模块。\n --add-exports /=(,)*\n 更新 以将 导出到 ,\n 而无论模块如何声明。\n 可以是 ALL-UNNAMED,将导出到所有\n 未命名模块。\n --add-opens /=(,)*\n 更新 以在 中打开\n ,而无论模块如何声明。\n --limit-modules [,...]\n 限制可观察模块的领域\n --patch-module =({0})*\n 使用 JAR 文件或目录中的类和资源\n 覆盖或增强模块。\n --source \n 设置源文件模式中源的版本。\n --finalization=\n 控制 JVM 是否执行对象最终处理,\n 其中 为 "enabled" 或 "disabled" 之一。\n 默认情况下,最终处理处于启用状态。\n --sun-misc-unsafe-memory-access=\n 允许或拒绝使用不受支持的 API sun.misc.Unsafe\n 为 "allow"、"warn"、"debug" 或 "deny" 之一。\n 默认值为 "warn"。\n\n这些额外选项如有更改, 恕不另行通知。\n +相关的安全设置并继续\n -XshowSettings:system\n (仅 Linux)显示主机系统或容器\n 配置并继续\n -Xss 设置 Java 线程堆栈大小\n 实际大小可以舍入到\n 操作系统要求的系统页面大小的倍数。\n -Xverify 设置字节码验证器的模式\n --add-reads =(,)*\n 更新 以读取 ,而无论\n 模块如何声明。 \n 可以是 ALL-UNNAMED,将读取所有未命名\n 模块。\n --add-exports /=(,)*\n 更新 以将 导出到 ,\n 而无论模块如何声明。\n 可以是 ALL-UNNAMED,将导出到所有\n 未命名模块。\n --add-opens /=(,)*\n 更新 以在 中打开\n ,而无论模块如何声明。\n --limit-modules [,...]\n 限制可观察模块的领域\n --patch-module =({0})*\n 使用 JAR 文件或目录中的类和资源\n 覆盖或增强模块。\n --source \n 设置源文件模式中源的版本。\n --finalization=\n 控制 JVM 是否执行对象最终处理,\n 其中 为 "enabled" 或 "disabled" 之一。\n 默认情况下,最终处理处于启用状态。\n --sun-misc-unsafe-memory-access=\n 允许或拒绝使用不受支持的 API sun.misc.Unsafe\n 为 "allow"、"warn"、"debug" 或 "deny" 之一。\n 默认值为 "warn"。\n\n这些额外选项如有更改, 恕不另行通知。\n # Translators please note do not translate the options themselves java.launcher.X.macosx.usage=\n以下选项是特定于 macOS 的选项:\n -XstartOnFirstThread\n 在第一个 (AppKit) 线程上运行 main() 方法\n -Xdock:name=\n 覆盖停靠栏中显示的默认应用程序名称\n -Xdock:icon=\n 覆盖停靠栏中显示的默认图标\n\n diff --git a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java index b40ed946648..ba804757e45 100644 --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java @@ -77,14 +77,14 @@ public class AnnotationParser { * Like {@link #parseAnnotations(byte[], sun.reflect.ConstantPool, Class)} * with an additional parameter {@code selectAnnotationClasses} which selects the * annotation types to parse (other than selected are quickly skipped).

    - * This method is used to parse select meta annotations in the construction + * This method is only used to parse select meta annotations in the construction * phase of {@link AnnotationType} instances to prevent infinite recursion. * * @param selectAnnotationClasses an array of annotation types to select when parsing */ @SafeVarargs @SuppressWarnings("varargs") // selectAnnotationClasses is used safely - public static Map, Annotation> parseSelectAnnotations( + static Map, Annotation> parseSelectAnnotations( byte[] rawAnnotations, ConstantPool constPool, Class container, diff --git a/src/java.base/share/classes/sun/security/util/RawKeySpec.java b/src/java.base/share/classes/sun/security/internal/InternalBinaryEncodable.java similarity index 61% rename from src/java.base/share/classes/sun/security/util/RawKeySpec.java rename to src/java.base/share/classes/sun/security/internal/InternalBinaryEncodable.java index 8e811573453..72d0008a505 100644 --- a/src/java.base/share/classes/sun/security/util/RawKeySpec.java +++ b/src/java.base/share/classes/sun/security/internal/InternalBinaryEncodable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -23,30 +23,17 @@ * questions. */ -package sun.security.util; +package sun.security.internal; -import java.security.spec.KeySpec; +import java.security.BinaryEncodable; /** - * This is a KeySpec that is used to specify a key by its byte array implementation. - * It is intended to be used in testing algorithms where the algorithm specification - * describes the key in this form. + * This class is a non-public subtype of BinaryEncodable. This type + * allows the BinaryEncodable list of permitted subtypes to change + * over time without causing pre-existing switches to fail because of an + * unrecognized subtype. */ -public class RawKeySpec implements KeySpec { - private final byte[] keyArr; - /** - * The sole constructor. - * @param key contains the key as a byte array - */ - public RawKeySpec(byte[] key) { - keyArr = key.clone(); - } - /** - * Getter function. - * @return a copy of the key bits - */ - public byte[] getKeyArr() { - return keyArr.clone(); - } +public final class InternalBinaryEncodable implements BinaryEncodable { + private InternalBinaryEncodable() {} } 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 50afba7cab8..84b3ff2c33b 100644 --- a/src/java.base/share/classes/sun/security/provider/HSS.java +++ b/src/java.base/share/classes/sun/security/provider/HSS.java @@ -827,12 +827,6 @@ public final class HSS extends SignatureSpi { } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } - } else if (keySpec instanceof RawKeySpec rawSpec) { - try { - return new HSSPublicKey(rawSpec.getKeyArr(), false); - } catch (InvalidKeyException e) { - throw new InvalidKeySpecException(e); - } } throw new InvalidKeySpecException("Unrecognized KeySpec"); } @@ -866,17 +860,27 @@ public final class HSS extends SignatureSpi { if (key == null) { throw new InvalidKeyException("key cannot be null"); } + if (!(key instanceof PublicKey)) { + throw new InvalidKeyException("Only support public key"); + } PublicKey pKey; try { // Check if key originates from this factory if (key instanceof HSSPublicKey) { return key; } - // Convert key to spec - X509EncodedKeySpec x509EncodedKeySpec - = engineGetKeySpec(key, X509EncodedKeySpec.class); - // Create key from spec, and return it - pKey = engineGeneratePublic(x509EncodedKeySpec); + String format = key.getFormat(); + if ("X.509".equalsIgnoreCase(format)) { + // Convert key to spec + X509EncodedKeySpec x509EncodedKeySpec + = engineGetKeySpec(key, X509EncodedKeySpec.class); + // Create key from spec, and return it + pKey = engineGeneratePublic(x509EncodedKeySpec); + } else if ("RAW".equalsIgnoreCase(format)) { + pKey = new HSSPublicKey(key.getEncoded(), false); + } else { + throw new InvalidKeyException("Unknown format " + format); + } } catch (InvalidKeySpecException e) { throw new InvalidKeyException(e); } diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java index 9c4e2c898b6..e1b41817435 100644 --- a/src/java.base/share/classes/sun/security/provider/ML_DSA.java +++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.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 @@ -1184,7 +1184,7 @@ public class ML_DSA { allDone = false; while (!allDone) { allDone = true; - parXof.squeezeBlock(); + parXof.squeezeBlock(parInd); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java index 9099f1446ff..761d97aeb85 100644 --- a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java +++ b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.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 @@ -26,7 +26,6 @@ package sun.security.provider; import sun.security.pkcs.NamedPKCS8Key; -import sun.security.util.RawKeySpec; import sun.security.x509.NamedX509Key; import java.security.AsymmetricKey; @@ -53,7 +52,6 @@ import java.util.Arrays; /// 2. It writes to a RAW [EncodedKeySpec] if `getKeySpec(key, EncodedKeySpec.class)` /// is called. The format of the output is "RAW" and the algorithm is /// intentionally left unspecified. -/// 3. It reads from and writes to the internal type [RawKeySpec]. /// /// When reading from a RAW format, it needs enough info to derive the /// parameter set name. @@ -98,13 +96,6 @@ public abstract class NamedKeyFactory extends KeyFactorySpi { throw new InvalidKeySpecException(e); } } - case RawKeySpec rks -> { - if (pnames.length == 1) { - yield new NamedX509Key(fname, pnames[0], rks.getKeyArr()); - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); - } - } case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> { if (pnames.length == 1) { yield new NamedX509Key(fname, pnames[0], espec.getEncoded()); @@ -134,18 +125,6 @@ public abstract class NamedKeyFactory extends KeyFactorySpi { Arrays.fill(bytes, (byte) 0); } } - case RawKeySpec rks -> { - if (pnames.length == 1) { - var raw = rks.getKeyArr(); - try { - yield fromRaw(pnames[0], raw); - } catch (InvalidKeyException e) { - throw new InvalidKeySpecException("Invalid key input", e); - } - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); - } - } case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> { if (pnames.length == 1) { var raw = espec.getEncoded(); @@ -212,8 +191,6 @@ public abstract class NamedKeyFactory extends KeyFactorySpi { if (keySpec == PKCS8EncodedKeySpec.class) { return keySpec.cast( new PKCS8EncodedKeySpec(bytes = key.getEncoded())); - } else if (keySpec == RawKeySpec.class) { - return keySpec.cast(new RawKeySpec(nk.getRawBytes())); } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) { return keySpec.cast( new RawEncodedKeySpec(nk.getRawBytes())); @@ -229,8 +206,6 @@ public abstract class NamedKeyFactory extends KeyFactorySpi { if (keySpec == X509EncodedKeySpec.class && key.getFormat().equalsIgnoreCase("X.509")) { return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); - } else if (keySpec == RawKeySpec.class) { - return keySpec.cast(new RawKeySpec(nk.getRawBytes())); } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) { return keySpec.cast(new RawEncodedKeySpec(nk.getRawBytes())); } else { diff --git a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java index caf6a7a2899..0fcc91542fa 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.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 @@ -80,9 +80,28 @@ public class SHA3Parallel { } } - public int squeezeBlock() { - int retVal = quadKeccak(lanesArr[0], lanesArr[1], lanesArr[2], lanesArr[3]); - for (int i = 0; i < NRPAR; i++) { + public int squeezeBlock(int nr) throws InvalidAlgorithmParameterException { + int retVal = 0; + switch (nr) { + case 1: + // until we enable single keccak intrinsic, use the better + // doubleKeccak + case 2: + retVal = doubleKeccak(lanesArr[0], lanesArr[1]); + break; + case 3: + // until we enable single keccak intrinsic, use the better + // doubleKeccak/quadKeccak + case 4: + retVal = quadKeccak(lanesArr[0], lanesArr[1], lanesArr[2], + lanesArr[3]); + break; + default: + throw new InvalidAlgorithmParameterException( + "Bad parallel parameter."); + } + + for (int i = 0; i < nr; i++) { l2bLittle(lanesArr[i], 0, buffers[i], 0, blockSize); } return retVal; 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 ef5c5b82f06..9f860af101e 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java @@ -268,17 +268,17 @@ public class DHasKEM implements KEMSpi { // RFC 8446 section 7.4.2: checks for all-zero // X25519/X448 shared secret. - if (kaAlgorithm.equals("X25519") || - kaAlgorithm.equals("X448")) { + if (this == X25519 || this == X448) { byte[] s = secret.getEncoded(); + byte data = 0; for (byte b : s) { - if (b != 0) { - return secret; - } + data |= b; + } + if (data == 0) { + // Trigger ILLEGAL_PARAMETER alert + throw new IllegalArgumentException( + "All-zero shared 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/Finished.java b/src/java.base/share/classes/sun/security/ssl/Finished.java index bf2d2230719..7ca9f50be97 100644 --- a/src/java.base/share/classes/sun/security/ssl/Finished.java +++ b/src/java.base/share/classes/sun/security/ssl/Finished.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 @@ -562,7 +562,7 @@ final class Finished { // handshake context cleanup. chc.handshakeFinished = true; - recordEvent(chc.conContext.conSession); + recordEvent(chc); // May need to retransmit the last flight for DTLS. if (!chc.sslContext.isDTLS()) { @@ -623,7 +623,7 @@ final class Finished { // handshake context cleanup. shc.handshakeFinished = true; - recordEvent(shc.conContext.conSession); + recordEvent(shc); // May need to retransmit the last flight for DTLS. if (!shc.sslContext.isDTLS()) { @@ -765,7 +765,7 @@ final class Finished { // handshake context cleanup. chc.handshakeFinished = true; chc.conContext.finishHandshake(); - recordEvent(chc.conContext.conSession); + recordEvent(chc); // The handshake message has been delivered. @@ -1164,7 +1164,7 @@ final class Finished { if (!shc.sslContext.isDTLS()) { shc.conContext.finishHandshake(); } - recordEvent(shc.conContext.conSession); + recordEvent(shc); // // produce @@ -1174,9 +1174,10 @@ final class Finished { } } - private static void recordEvent(SSLSessionImpl session) { + private static void recordEvent(HandshakeContext hc) { TLSHandshakeEvent event = new TLSHandshakeEvent(); if (event.shouldCommit() || EventHelper.isLoggingSecurity()) { + SSLSessionImpl session = hc.conContext.conSession; int hash = 0; try { // use hash code for Id @@ -1187,12 +1188,20 @@ final class Finished { // not verified msg } long peerCertificateId = Integer.toUnsignedLong(hash); + String namedGroup = "N/A"; + for (SSLCredentials cred : hc.handshakeCredentials) { + if (cred instanceof NamedGroupCredentials ngCred) { + namedGroup = ngCred.getNamedGroup().name; + break; + } + } if (event.shouldCommit()) { event.peerHost = session.getPeerHost(); event.peerPort = session.getPeerPort(); event.cipherSuite = session.getCipherSuite(); event.protocolVersion = session.getProtocol(); event.certificateId = peerCertificateId; + event.namedGroup = namedGroup; event.commit(); } if (EventHelper.isLoggingSecurity()) { @@ -1201,6 +1210,7 @@ final class Finished { session.getPeerPort(), session.getCipherSuite(), session.getProtocol(), + namedGroup, peerCertificateId); } } diff --git a/src/java.base/share/classes/sun/security/ssl/Hybrid.java b/src/java.base/share/classes/sun/security/ssl/Hybrid.java index 43634ce2f34..2d00973318a 100644 --- a/src/java.base/share/classes/sun/security/ssl/Hybrid.java +++ b/src/java.base/share/classes/sun/security/ssl/Hybrid.java @@ -28,7 +28,7 @@ package sun.security.ssl; import sun.security.util.ArrayUtil; import sun.security.util.CurveDB; import sun.security.util.ECUtil; -import sun.security.util.RawKeySpec; +import sun.security.util.KeyUtil; import sun.security.x509.X509Key; import javax.crypto.DecapsulateException; @@ -171,67 +171,19 @@ public class Hybrid { @Override protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { - if (keySpec == null) { - throw new InvalidKeySpecException("keySpec must not be null"); - } + throw new InvalidKeySpecException("Not supported"); + } - if (keySpec instanceof RawKeySpec rks) { - byte[] key = rks.getKeyArr(); - if (key == null) { - throw new InvalidKeySpecException( - "RawkeySpec contains null key data"); - } - if (key.length <= leftlen) { - throw new InvalidKeySpecException( - "Hybrid key length " + key.length + - " is too short and its left key length is " + - leftlen); - } + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws + InvalidKeySpecException { + throw new InvalidKeySpecException("Not supported"); + } - byte[] leftKeyBytes = Arrays.copyOfRange(key, 0, leftlen); - byte[] rightKeyBytes = Arrays.copyOfRange(key, leftlen, - key.length); - PublicKey leftKey, rightKey; - - try { - if (leftname.startsWith("secp")) { - var curve = CurveDB.lookup(leftname); - var ecSpec = new ECPublicKeySpec( - ECUtil.decodePoint(leftKeyBytes, - curve.getCurve()), curve); - leftKey = left.generatePublic(ecSpec); - } else if (leftname.startsWith("ML-KEM")) { - leftKey = left.generatePublic(new RawKeySpec( - leftKeyBytes)); - } else { - throw new InvalidKeySpecException("Unsupported left" + - " algorithm" + leftname); - } - - if (rightname.equals("X25519")) { - ArrayUtil.reverse(rightKeyBytes); - var xecSpec = new XECPublicKeySpec( - new NamedParameterSpec(rightname), - new BigInteger(1, rightKeyBytes)); - rightKey = right.generatePublic(xecSpec); - } else if (rightname.startsWith("ML-KEM")) { - rightKey = right.generatePublic(new RawKeySpec( - rightKeyBytes)); - } else { - throw new InvalidKeySpecException("Unsupported right" + - " algorithm: " + rightname); - } - - return new PublicKeyImpl("Hybrid", leftKey, rightKey); - } catch (Exception e) { - throw new InvalidKeySpecException("Failed to decode " + - "hybrid key", e); - } - } - - throw new InvalidKeySpecException( - "KeySpec type:" + - keySpec.getClass().getName() + " not supported"); + @Override + protected T engineGetKeySpec(Key key, + Class keySpec) throws InvalidKeySpecException { + throw new InvalidKeySpecException("Not supported"); } private static int leftPublicLength(String name) { @@ -245,20 +197,83 @@ public class Hybrid { } @Override - protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws - InvalidKeySpecException { - throw new UnsupportedOperationException(); - } + protected Key engineTranslateKey(Key inKey) throws InvalidKeyException { + if (inKey == null) { + throw new InvalidKeyException("key must not be null"); + } - @Override - protected T engineGetKeySpec(Key key, - Class keySpec) throws InvalidKeySpecException { - throw new UnsupportedOperationException(); - } + if (inKey instanceof PublicKey + && "RAW".equalsIgnoreCase(inKey.getFormat())) { + byte[] key = inKey.getEncoded(); + if (key == null) { + throw new InvalidKeyException( + "Key contains null key data"); + } + if (key.length <= leftlen) { + throw new InvalidKeyException( + "Hybrid key length " + key.length + + " is too short and its left key length is " + + leftlen); + } - @Override - protected Key engineTranslateKey(Key key) throws InvalidKeyException { - throw new UnsupportedOperationException(); + byte[] leftKeyBytes = Arrays.copyOfRange(key, 0, leftlen); + byte[] rightKeyBytes = Arrays.copyOfRange(key, leftlen, + key.length); + PublicKey leftKey, rightKey; + + try { + if (leftname.startsWith("secp")) { + var curve = CurveDB.lookup(leftname); + var ecSpec = new ECPublicKeySpec( + ECUtil.decodePoint(leftKeyBytes, + curve.getCurve()), curve); + leftKey = left.generatePublic(ecSpec); + } else if (leftname.startsWith("ML-KEM")) { + try { + leftKey = (PublicKey) left.translateKey(KeyUtil + .newRawPublicKey(leftname, leftKeyBytes)); + } catch (InvalidKeyException e) { + // Fallback to X.509 encoding if ML-KEM impl + // does not support translating from RAW + leftKey = left.generatePublic(new X509EncodedKeySpec( + KeyUtil.rawToX509(leftname, leftKeyBytes))); + } + } else { + throw new InvalidKeySpecException("Unsupported left" + + " algorithm" + leftname); + } + + if (rightname.equals("X25519")) { + ArrayUtil.reverse(rightKeyBytes); + var xecSpec = new XECPublicKeySpec( + new NamedParameterSpec(rightname), + new BigInteger(1, rightKeyBytes)); + rightKey = right.generatePublic(xecSpec); + } else if (rightname.startsWith("ML-KEM")) { + try { + rightKey = (PublicKey) right.translateKey(KeyUtil + .newRawPublicKey(rightname, rightKeyBytes)); + } catch (InvalidKeyException e) { + // Fallback to X.509 encoding if ML-KEM impl + // does not support translating from RAW + rightKey = right.generatePublic(new X509EncodedKeySpec( + KeyUtil.rawToX509(rightname, rightKeyBytes))); + } + } else { + throw new InvalidKeySpecException("Unsupported right" + + " algorithm: " + rightname); + } + + return new PublicKeyImpl("Hybrid", leftKey, rightKey); + } catch (Exception e) { + throw new InvalidKeyException("Failed to decode " + + "hybrid key", e); + } + } else { + throw new InvalidKeyException("Unknown key " + + inKey.getClass().getName() + " in " + + inKey.getFormat()); + } } } 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 0ca197160a9..95b021b0f6e 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -24,8 +24,6 @@ */ package sun.security.ssl; -import sun.security.util.RawKeySpec; - import javax.crypto.DecapsulateException; import javax.crypto.KDF; import javax.crypto.KEM; @@ -42,6 +40,8 @@ import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.spec.X509EncodedKeySpec; + import sun.security.util.KeyUtil; /** @@ -185,7 +185,21 @@ public class KAKeyDerivation implements SSLKeyDerivation { KeyFactory kf = (provider != null) ? KeyFactory.getInstance(algorithmName, provider) : KeyFactory.getInstance(algorithmName); - var pk = kf.generatePublic(new RawKeySpec(keyshare)); + PublicKey pk; + try { + pk = (PublicKey) kf.translateKey( + KeyUtil.newRawPublicKey(algorithmName, keyshare)); + } catch (InvalidKeyException e) { + // Fallback to X.509 encoding if ML-KEM impl + // does not support translating from RAW + try { + pk = kf.generatePublic(new X509EncodedKeySpec( + KeyUtil.rawToX509(algorithmName, keyshare))); + } catch (GeneralSecurityException e2) { + e2.addSuppressed(e); + throw new InvalidKeyException(e2); + } + } KEM kem = (provider != null) ? KEM.getInstance(algorithmName, provider) : diff --git a/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java index fb8de6cb104..2ce3c57e412 100644 --- a/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java @@ -37,6 +37,7 @@ import java.security.spec.NamedParameterSpec; import javax.crypto.SecretKey; import sun.security.ssl.NamedGroup.NamedGroupSpec; +import sun.security.util.KeyUtil; import sun.security.x509.X509Key; /** @@ -140,10 +141,20 @@ final class KEMKeyExchange { public byte[] encode() { if (publicKey instanceof X509Key xk) { return xk.getKeyAsBytes(); - } else if (publicKey instanceof Hybrid.PublicKeyImpl hk) { - return hk.getEncoded(); + } else { + String format = publicKey.getFormat(); + if ("RAW".equalsIgnoreCase(format)) { + return publicKey.getEncoded(); + } else if ("X.509".equalsIgnoreCase(format)) { + try { + return KeyUtil.x509ToRaw(publicKey.getEncoded()); + } catch (IOException e) { + throw new ProviderException("Invalid X.509 format"); + } + } else { + throw new ProviderException("Unknown format " + format); + } } - throw new ProviderException("Unsupported key type: " + publicKey); } // Package-private diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java index 565ed8f6128..61b1236e9bc 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,9 +173,18 @@ class SSLAlgorithmDecomposer extends AlgorithmDecomposer { case B_AES_128_GCM: components.add("AES_128_GCM"); break; + case B_AES_128_GCM_IV: + components.add("AES_128_GCM"); + break; case B_AES_256_GCM: components.add("AES_256_GCM"); break; + case B_AES_256_GCM_IV: + components.add("AES_256_GCM"); + break; + case B_CC20_P1305: + components.add("CHACHA20_POLY1305"); + break; } return components; diff --git a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties index a452dd34e9d..479c536b7c8 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties +++ b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties @@ -52,7 +52,7 @@ Imports.entries.from.a.JDK.1.1.x.style.identity.database=Importiert Einträge au Imports.a.certificate.or.a.certificate.chain=Importiert ein Zertifikat oder eine Zertifikatskette Imports.a.password=Importiert ein Kennwort Imports.one.or.all.entries.from.another.keystore=Importiert einen oder alle Einträge aus einem anderen Keystore -Clones.a.key.entry=Clont einen Schlüsseleintrag +Clones.a.key.entry=Klont einen Schlüsseleintrag Changes.the.key.password.of.an.entry=Ändert das Schlüsselkennwort eines Eintrags Lists.entries.in.a.keystore=Listet die Einträge in einem Keystore auf Prints.the.content.of.a.certificate=Druckt den Content eines Zertifikats diff --git a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties index 435e74e468f..622b7545183 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties +++ b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties @@ -114,7 +114,7 @@ verbose.output=详细输出 validity.number.of.days=有效天数 Serial.ID.of.cert.to.revoke=要撤销的证书的序列 ID # keytool: Running part -keytool.error.=keytool 错误:\u0020 +keytool.error.=keytool 错误: Illegal.option.=非法选项: \u0020 Illegal.value.=非法值:\u0020 Unknown.password.type.=未知口令类型:\u0020 @@ -216,7 +216,7 @@ Do.you.still.want.to.add.it.no.=是否仍要添加? [否]: \u0020 Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.=在别名 <{0}> 之下, 证书已经存在于系统范围的 CA 密钥库中 Do.you.still.want.to.add.it.to.your.own.keystore.no.=是否仍要将它添加到自己的密钥库? [否]: \u0020 Trust.this.certificate.no.=是否信任此证书? [否]: \u0020 -New.prompt.=新 {0}:\u0020 +New.prompt.=新 {0}: Passwords.must.differ=口令不能相同 Re.enter.new.prompt.=重新输入新{0}:\u0020 Re.enter.password.=再次输入口令:\u0020 @@ -269,7 +269,7 @@ Please.provide.keysize.for.secret.key.generation=请提供 -keysize 以生成密 warning.not.verified.make.sure.keystore.is.correct=警告: 未验证。请确保密钥库是正确的。 warning.not.verified.make.sure.keystore.is.correct.or.specify.trustcacerts=警告:未验证。请确保密钥库是正确的,或者指定 -trustcacerts。 -Extensions.=扩展:\u0020 +Extensions.=扩展: .Empty.value.=(空值) Extension.Request.=扩展请求: Unknown.keyUsage.type.=未知 keyUsage 类型:\u0020 diff --git a/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java index ad3beab350f..781c1ab2cd2 100644 --- a/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java @@ -26,7 +26,9 @@ package sun.security.util; import java.lang.ref.SoftReference; +import java.net.URL; import java.security.AlgorithmParameters; +import java.security.CodeSource; import java.security.CryptoPrimitive; import java.security.Key; import java.util.Arrays; @@ -36,9 +38,10 @@ import java.util.concurrent.ConcurrentHashMap; /** * This class implements the algorithm constraints for the - * "jdk.crypto.disabledAlgorithms" security property. This security property - * can be overridden by the system property of the same name. See the - * java.security file for the syntax of the property value. + * "jdk.crypto.disabledAlgorithms" and "jdk.crypto.legacyAlgorithms" security + * properties. Each security property can be overridden by a system property + * of the same name. See the java.security file for the syntax of the property + * values. */ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { private static final Debug debug = Debug.getInstance("jca"); @@ -51,11 +54,20 @@ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { private static final String PROPERTY_CRYPTO_DISABLED_ALGS = "jdk.crypto.disabledAlgorithms"; - private static class CryptoHolder { - static final CryptoAlgorithmConstraints CONSTRAINTS = + // Legacy algorithm security property for JCE crypto services + private static final String PROPERTY_CRYPTO_LEGACY_ALGS = + "jdk.crypto.legacyAlgorithms"; + + private static class DisabledHolder { + private static final CryptoAlgorithmConstraints DISABLED_CONSTRAINTS = new CryptoAlgorithmConstraints(PROPERTY_CRYPTO_DISABLED_ALGS); } + private static class LegacyHolder { + private static final CryptoAlgorithmConstraints LEGACY_CONSTRAINTS = + new CryptoAlgorithmConstraints(PROPERTY_CRYPTO_LEGACY_ALGS); + } + private static void debug(String msg) { if (debug != null) { debug.println("CryptoAlgoConstraints: ", msg); @@ -63,11 +75,47 @@ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { } public static boolean permits(String service, String algo) { - return CryptoHolder.CONSTRAINTS.cachedCheckAlgorithm( + return DisabledHolder.DISABLED_CONSTRAINTS.cachedCheckAlgorithm( service + "." + algo); } - private final Set disabledServices; // syntax is . + public static boolean isLegacy(String service, String alg) { + return !LegacyHolder.LEGACY_CONSTRAINTS.cachedCheckAlgorithm( + service + "." + alg); + } + + private static class CallersHolder { + static final ClassValue> callers = new ClassValue<>() { + @Override + protected Set computeValue(Class type) { + return ConcurrentHashMap.newKeySet(); + } + }; + } + + public static void warn(String service, String alg, Class callerClass) { + if (callerClass == null) { + callerClass = CryptoAlgorithmConstraints.class; + } + String serviceAndAlg = service + "." + alg; + Set warnedAlgorithms = CallersHolder.callers.get(callerClass); + if (warnedAlgorithms.add(serviceAndAlg)) { + URL url = codeSource(callerClass); + String source = (url == null) ? callerClass.getName() : + callerClass.getName() + " (" + url + ")"; + System.err.printf(""" + WARNING: An outdated %s algorithm has been called by %s + WARNING: %s will be disabled by default in a future release + """, service, source, alg); + } + } + + private static URL codeSource(Class clazz) { + CodeSource cs = clazz.getProtectionDomain().getCodeSource(); + return (cs != null) ? cs.getLocation() : null; + } + + private final Set affectedServices; // syntax is . private volatile SoftReference> cacheRef = new SoftReference<>(null); @@ -76,42 +124,42 @@ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { * {@code propertyName}. Note that if a system property of the same name * is set, it overrides the security property. * - * @param propertyName the security property name that define the disabled + * @param propertyName the security property name that defines the * algorithm constraints */ CryptoAlgorithmConstraints(String propertyName) { super(null); - disabledServices = getAlgorithms(propertyName, true); - String[] entries = disabledServices.toArray(new String[0]); + affectedServices = getAlgorithms(propertyName, true); + String[] entries = affectedServices.toArray(new String[0]); debug("Before " + Arrays.deepToString(entries)); - for (String dk : entries) { - int idx = dk.indexOf("."); - if (idx < 1 || idx == dk.length() - 1) { + for (String k : entries) { + int idx = k.indexOf("."); + if (idx < 1 || idx == k.length() - 1) { // wrong syntax: missing "." or empty service or algorithm - throw new IllegalArgumentException("Invalid entry: " + dk); + throw new IllegalArgumentException("Invalid entry: " + k); } - String service = dk.substring(0, idx); - String algo = dk.substring(idx + 1); + String service = k.substring(0, idx); + String algo = k.substring(idx + 1); if (SUPPORTED_SERVICES.stream().anyMatch(e -> e.equalsIgnoreCase (service))) { KnownOIDs oid = KnownOIDs.findMatch(algo); if (oid != null) { debug("Add oid: " + oid.value()); - disabledServices.add(service + "." + oid.value()); + affectedServices.add(service + "." + oid.value()); debug("Add oid stdName: " + oid.stdName()); - disabledServices.add(service + "." + oid.stdName()); + affectedServices.add(service + "." + oid.stdName()); for (String a : oid.aliases()) { debug("Add oid alias: " + a); - disabledServices.add(service + "." + a); + affectedServices.add(service + "." + a); } } } else { // unsupported service - throw new IllegalArgumentException("Invalid entry: " + dk); + throw new IllegalArgumentException("Invalid entry: " + k); } } - debug("After " + Arrays.deepToString(disabledServices.toArray())); + debug("After " + Arrays.deepToString(affectedServices.toArray())); } @Override @@ -131,7 +179,7 @@ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { throw new UnsupportedOperationException("Unsupported permits() method"); } - // Return false if algorithm is found in the disabledServices Set. + // Return false if algorithm is found in the affectedServices Set. // Otherwise, return true. private boolean cachedCheckAlgorithm(String serviceDesc) { Map cache; @@ -147,7 +195,7 @@ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { if (result != null) { return result; } - result = checkAlgorithm(disabledServices, serviceDesc, null); + result = checkAlgorithm(affectedServices, serviceDesc, null); cache.put(serviceDesc, result); return result; } 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 5a14deb70a4..0c0bc134353 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -46,6 +46,7 @@ import com.sun.crypto.provider.PBKDF2KeyImpl; import sun.security.jca.JCAUtil; import sun.security.pkcs.PKCS8Key; import sun.security.x509.AlgorithmId; +import sun.security.x509.X509Key; /** * A utility class to get key length, validate keys, etc. @@ -589,5 +590,66 @@ public final class KeyUtil { } } } + + public static PublicKey newRawPublicKey(String algorithm, byte[] key) { + return newRawPublicKey(algorithm, null, key); + } + + public static PublicKey newRawPublicKey(String algorithm, + AlgorithmParameterSpec params, byte[] key) { + return new RawPublicKey(algorithm, params, key); + } + + private record RawPublicKey(String algorithm, AlgorithmParameterSpec params, + byte[] data) implements PublicKey { + + RawPublicKey { + data = data.clone(); + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getFormat() { + return "RAW"; + } + + @Override + public byte[] getEncoded() { + return data.clone(); + } + + @Override + public AlgorithmParameterSpec getParams() { + return params; + } + } + + // Convert RAW encoding to X.509 encoding of a public key. + // The AlgorithmId will be a single OID from `pname`, so this + // cannot be used by EC or RSASSA-PSS. + static public byte[] rawToX509(String pname, byte[] bytes) + throws NoSuchAlgorithmException { + return new X509Key(AlgorithmId.get(pname), + new BitArray(bytes.length * 8, bytes)).getEncoded(); + } + + // Convert X.509 encoding to RAW encoding of a public key. + // AlgorithmId is ignored. No check for trailing data after key. + static public byte[] x509ToRaw(byte[] bytes) throws IOException { + DerValue in = new DerValue(bytes); + if (in.tag != DerValue.tag_Sequence) { + throw new IOException("corrupt subject key"); + } + AlgorithmId.parse(in.data.getDerValue()); + BitArray keyMaterial = in.data.getUnalignedBitString(); + if (keyMaterial.length() % 8 != 0) { + throw new IOException("Unaligned bits in public key"); + } + return keyMaterial.toByteArray(); + } } diff --git a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java index 61062bf6e1a..8d79e85c8a5 100644 --- a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java +++ b/src/java.base/share/classes/sun/security/util/LocalizedMessage.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 @@ -106,32 +106,33 @@ public class LocalizedMessage { // Classes like StringTokenizer may not be loaded, so parsing // is performed with String methods StringBuilder sb = new StringBuilder(); - int nextBraceIndex; - while ((nextBraceIndex = value.indexOf('{')) >= 0) { + int pos = 0; + int leftBraceIndex; + while ((leftBraceIndex = value.indexOf('{', pos)) >= 0) { - String firstPart = value.substring(0, nextBraceIndex); - sb.append(firstPart); - value = value.substring(nextBraceIndex + 1); + sb.append(value, pos, leftBraceIndex); // look for closing brace and argument index - nextBraceIndex = value.indexOf('}'); - if (nextBraceIndex < 0) { + int rightBraceIndex = value.indexOf('}', leftBraceIndex + 1); + if (rightBraceIndex < 0) { // no closing brace // MessageFormat would throw IllegalArgumentException, but // that exception class may not be loaded yet throw new RuntimeException("Unmatched braces"); } - String indexStr = value.substring(0, nextBraceIndex); try { - int index = Integer.parseInt(indexStr); + int index = Integer.parseInt(value, leftBraceIndex + 1, + rightBraceIndex, 10); sb.append(arguments[index]); } catch (NumberFormatException e) { // argument index is not an integer - throw new RuntimeException("not an integer: " + indexStr); + throw new RuntimeException("not an integer: " + + value.substring(leftBraceIndex + 1, rightBraceIndex)); } - value = value.substring(nextBraceIndex + 1); + + pos = rightBraceIndex + 1; } - sb.append(value); + sb.append(value, pos, value.length()); return sb.toString(); } diff --git a/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties b/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties index 663b3f8993f..209ebcecfa7 100644 --- a/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties +++ b/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties @@ -53,8 +53,8 @@ Configuration.Error.Line.line.expected.expect.=配置错误: \n\t行 {0}: 应为 Configuration.Error.Line.line.system.property.value.expanded.to.empty.value=配置错误: \n\t行 {0}: 系统属性 [{1}] 扩展到空值 # com.sun.security.auth.module.JndiLoginModule -username.=用户名:\u0020 -password.=密码:\u0020 +username.=用户名: +password.=密码: # com.sun.security.auth.module.KeyStoreLoginModule Please.enter.keystore.information=请输入密钥库信息 @@ -63,5 +63,5 @@ Keystore.password.=密钥库口令:\u0020 Private.key.password.optional.=私有密钥口令 (可选):\u0020 # com.sun.security.auth.module.Krb5LoginModule -Kerberos.username.defUsername.=Kerberos 用户名 [{0}]:\u0020 -Kerberos.password.for.username.={0} 的 Kerberos 密码:\u0020 +Kerberos.username.defUsername.=Kerberos 用户名 [{0}]: +Kerberos.password.for.username.={0} 的 Kerberos 密码: diff --git a/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties b/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties index a322cb7b1e8..d86f05ecb09 100644 --- a/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties +++ b/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties @@ -44,16 +44,16 @@ NEWLINE=\n invalid.null.action.provided=提供了无效的空操作 invalid.null.Class.provided=提供了无效的空类 Subject.=主体:\n -.Principal.=\t主用户:\u0020 -.Public.Credential.=\t公共身份证明:\u0020 -.Private.Credential.=\t专用身份证明:\u0020 +.Principal.=\t主用户: +.Public.Credential.=\t公共身份证明: +.Private.Credential.=\t专用身份证明: .Private.Credential.inaccessible.=\t无法访问专用身份证明\n Subject.is.read.only=主体为只读 attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set=正在尝试将一个非 java.security.Principal 实例的对象添加到主体的主用户集中 attempting.to.add.an.object.which.is.not.an.instance.of.class=正在尝试添加一个非{0}实例的对象 # javax.security.auth.login.AppConfigurationEntry -LoginModuleControlFlag.=LoginModuleControlFlag:\u0020 +LoginModuleControlFlag.=LoginModuleControlFlag: # javax.security.auth.login.LoginContext Invalid.null.input.name=无效空输入: 名称 @@ -73,7 +73,7 @@ line.number.msg=第 {0} 行:{1} line.number.expected.expect.found.actual.=行号 {0}: 应为 [{1}], 找到 [{2}] # sun.security.pkcs11.SunPKCS11 -PKCS11.Token.providerName.Password.=PKCS11 标记 [{0}] 密码:\u0020 +PKCS11.Token.providerName.Password.=PKCS11 标记 [{0}] 密码: # sun.security.util.Password warning.input.may.be.visible.on.screen=[警告:输入可能显示在屏幕上]\u0020 diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java index 573187ba3d0..1e80bce3839 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.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 @@ -288,6 +288,10 @@ public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter { || langtags.contains(getEquivalentLoc(locale).toLanguageTag()); } + public Set baseModuleLocales() { + return baseMetaInfo.baseModuleLocales(); + } + /** * Returns the canonical ID for the given ID */ diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 31078720ddc..295952e7896 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -275,4 +275,10 @@ public final class BaseLocale { } return h; } + + // This is called from C code, at the very end of Java code execution + // during the AOT cache assembly phase. + private static void assemblySetup() { + CACHE.get().prepareForAOTCache(); + } } diff --git a/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java b/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java index 5da725d59c8..499cb757125 100644 --- a/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java +++ b/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.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 @@ -380,27 +380,31 @@ public final class InternalLocaleBuilder { String variant = base.getVariant(); // Special backward compatibility support - - // Exception 1 - ja_JP_JP - if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) { - // When locale ja_JP_JP is created, ca-japanese is always there. - // The builder ignores the variant "JP" - assert("japanese".equals(localeExtensions.getUnicodeLocaleType("ca"))); - variant = ""; - } - // Exception 2 - th_TH_TH - else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) { - // When locale th_TH_TH is created, nu-thai is always there. - // The builder ignores the variant "TH" - assert("thai".equals(localeExtensions.getUnicodeLocaleType("nu"))); - variant = ""; - } - // Exception 3 - no_NO_NY - else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { - // no_NO_NY is a valid locale and used by Java 6 or older versions. - // The build ignores the variant "NY" and change the language to "nn". - language = "nn"; - variant = ""; + if (script.isEmpty()) { + // Exception 1 - ja_JP_JP + if (language.equals("ja") && region.equals("JP") && variant.equals("JP") + && LocaleExtensions.CALENDAR_JAPANESE.equals(localeExtensions)) { + // When locale ja_JP_JP is created, ca-japanese is always added. + // If the extension exists, the builder ignores the variant "JP" + // otherwise "JP" is merely an ill-formed variant + variant = ""; + } + // Exception 2 - th_TH_TH + else if (language.equals("th") && region.equals("TH") && variant.equals("TH") + && LocaleExtensions.NUMBER_THAI.equals(localeExtensions)){ + // When locale th_TH_TH is created, nu-thai is always added. + // If the extension exists, the builder ignores the variant "TH" + // otherwise "TH" is merely an ill-formed variant + variant = ""; + } + // Exception 3 - no_NO_NY + else if (language.equals("no") && region.equals("NO") && variant.equals("NY") + && localeExtensions == null) { + // no_NO_NY is a valid locale and used by Java 6 or older versions. + // The builder ignores the variant "NY" and changes the language to "nn". + language = "nn"; + variant = ""; + } } // Validate base locale fields before updating internal state. diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java index 0b2fee7f2cd..485fb7f5ca6 100644 --- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java +++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.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 @@ -120,7 +120,7 @@ public record LanguageTag(String language, List extensions; // langtag must start with either language or privateuse if (!language.isEmpty()) { - extlangs = parseExtlangs(itr, pp); + extlangs = parseExtlangs(itr, pp, language); script = parseScript(itr, pp); region = parseRegion(itr, pp); variants = parseVariants(itr, pp); @@ -170,8 +170,11 @@ public record LanguageTag(String language, return EMPTY_SUBTAG; } - private static List parseExtlangs(StringTokenIterator itr, ParsePosition pp) { - if (itr.isDone() || pp.getErrorIndex() != -1) { + private static List parseExtlangs(StringTokenIterator itr, ParsePosition pp, String lang) { + var langLen = lang.length(); + if (itr.isDone() || pp.getErrorIndex() != -1 + // Extlangs only accepted after 2*3ALPHA lang + || (langLen != 2 && langLen != 3)) { return EMPTY_SUBTAGS; } List extlangs = null; @@ -415,7 +418,8 @@ public record LanguageTag(String language, } // Special handling for no_NO_NY - use nn_NO for language tag - if (language.equals("no") && region.equals("NO") && baseVariant.equals("NY")) { + if (language.equals("no") && region.equals("NO") && baseVariant.equals("NY") + && script.isEmpty() && localeExtensions == null) { language = "nn"; baseVariant = EMPTY_SUBTAG; } @@ -601,7 +605,7 @@ public record LanguageTag(String language, // / %x79-7A ; y - z return (s.length() == 1) - && LocaleUtils.isAlphaString(s) + && LocaleUtils.isAlphaNumericString(s) && !LocaleUtils.caseIgnoreMatch(PRIVATEUSE, s); } diff --git a/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java b/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java index bc5115e1ff1..5385a5598b6 100644 --- a/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java +++ b/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java @@ -467,17 +467,18 @@ public final class LocaleMatcher { try { w = Double.parseDouble(range.substring(index)); } - catch (Exception e) { - throw new IllegalArgumentException("weight=\"" + catch (NumberFormatException _) { + throw new IllegalArgumentException("The weight \"" + range.substring(index) - + "\" for language range \"" + r + "\""); + + "\" for language range \"" + r + "\"" + + " must be between " + MIN_WEIGHT + + " and " + MAX_WEIGHT + ", inclusive."); } - if (w < MIN_WEIGHT || w > MAX_WEIGHT) { - throw new IllegalArgumentException("weight=" + w - + " for language range \"" + r - + "\". It must be between " + MIN_WEIGHT - + " and " + MAX_WEIGHT + "."); + throw new IllegalArgumentException("The weight \"" + w + + "\" for language range \"" + r + "\"" + + " must be between " + MIN_WEIGHT + + " and " + MAX_WEIGHT + ", inclusive."); } } diff --git a/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java index 7b8b3b06eb3..2d6d95b509f 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java @@ -488,4 +488,8 @@ public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements R "th-TH-TH".equals(oldname) || "no-NO-NY".equals(oldname); } + + public Set baseModuleLocales() { + return Set.of(Locale.ROOT); + } } diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java index a0a0b6f5785..fc879282e20 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, 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 @@ -91,7 +91,7 @@ public class LocaleNameProviderImpl extends LocaleNameProvider implements Availa } /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 script code and the given locale that is appropriate for * display to the user. * For example, if scriptCode is "Latn" and locale diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java index cc9a805fe0d..f3d0990429d 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java @@ -370,16 +370,10 @@ public final class LocaleServiceProviderPool { locbld.clearExtensions(); lookupLocale = locbld.build(); } catch (IllformedLocaleException e) { - // A Locale with non-empty extensions - // should have well-formed fields except - // for ja_JP_JP and th_TH_TH. Therefore, - // it should never enter in this catch clause. - System.getLogger(LocaleServiceProviderPool.class.getCanonicalName()) - .log(System.Logger.Level.INFO, - "A locale(" + locale + ") has non-empty extensions, but has illformed fields."); - - // Fallback - script field will be lost. - lookupLocale = Locale.of(locale.getLanguage(), locale.getCountry(), locale.getVariant()); + // E.g. "en-Latn-US-a-foo-x-lvariant-xy" + // Extensions can exist while variant is ill-formed + // Simply strip the extensions so that all fields are preserved + lookupLocale = lookupLocale.stripExtensions(); } } return lookupLocale; diff --git a/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java index 613b1ee5158..48d9b832b13 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 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 @@ -27,6 +27,8 @@ package sun.util.locale.provider; import java.util.List; import java.util.Locale; +import java.util.Set; + import sun.util.resources.LocaleData; /** @@ -40,5 +42,11 @@ public interface ResourceBundleBasedAdapter { /** * candidate locales customization */ - public List getCandidateLocales(String baseName, Locale locale); + List getCandidateLocales(String baseName, Locale locale); + + /** + * Returns the locales whose resource bundles are resolved from + * the java.base module for this adapter. + */ + Set baseModuleLocales(); } diff --git a/src/java.base/share/classes/sun/util/resources/LocaleData.java b/src/java.base/share/classes/sun/util/resources/LocaleData.java index 20e8e0f8fe9..884f9610ca7 100644 --- a/src/java.base/share/classes/sun/util/resources/LocaleData.java +++ b/src/java.base/share/classes/sun/util/resources/LocaleData.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 @@ -45,7 +45,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.spi.ResourceBundleProvider; import sun.util.locale.provider.JRELocaleProviderAdapter; @@ -180,9 +179,6 @@ public class LocaleData { private static class LocaleDataStrategy implements Bundles.Strategy { private static final LocaleDataStrategy INSTANCE = new LocaleDataStrategy(); - // TODO: avoid hard-coded Locales - private static final Set JAVA_BASE_LOCALES - = Set.of(Locale.ROOT, Locale.ENGLISH, Locale.US, Locale.of("en", "US", "POSIX")); private LocaleDataStrategy() { } @@ -202,11 +198,8 @@ public class LocaleData { String key = baseName + '-' + locale.toLanguageTag(); List candidates = CANDIDATES_MAP.get(key); if (candidates == null) { - LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE; - LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); - candidates = adapter instanceof ResourceBundleBasedAdapter rbba ? - rbba.getCandidateLocales(baseName, locale) : - defaultControl.getCandidateLocales(baseName, locale); + var adapter = getAdapter(baseName); + candidates = adapter.getCandidateLocales(baseName, locale); // Weed out Locales which are known to have no resource bundles int lastDot = baseName.lastIndexOf('.'); @@ -227,7 +220,13 @@ public class LocaleData { } boolean inJavaBaseModule(String baseName, Locale locale) { - return JAVA_BASE_LOCALES.contains(locale); + return getAdapter(baseName).baseModuleLocales().contains(locale); + } + + private static ResourceBundleBasedAdapter getAdapter(String baseName) { + return (ResourceBundleBasedAdapter)(baseName.contains(DOTCLDR) ? + LocaleProviderAdapter.forType(CLDR) : + LocaleProviderAdapter.forType(JRE)); } @Override diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 976604b5cbc..26842d0c845 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -777,8 +777,8 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # In some environments, certain algorithms may be undesirable for certain # cryptographic services. For example, "MD2" is generally no longer considered # to be a secure hash algorithm. This section describes the mechanism for -# disabling algorithms at the JCA/JCE level based on service name and algorithm -# name. +# disabling algorithms and identifying legacy algorithms at the JCA/JCE +# level based on service name and algorithm name. # # If a system property of the same name is also specified, it supersedes the # security property value defined here. @@ -786,7 +786,10 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # The syntax of the disabled services string is described as follows: # "DisabledService {, DisabledService}" # -# DisabledService: +# The syntax of the legacy services string is described as follows: +# "LegacyService {, LegacyService}" +# +# DisabledService and LegacyService: # Service.AlgorithmName # # Service: (one of the following, more services may be added later) @@ -795,7 +798,7 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # AlgorithmName: # (see below) # -# The "AlgorithmName" is the standard algorithm name of the disabled +# The "AlgorithmName" is the standard algorithm name of the affected # service. See the Java Security Standard Algorithm Names Specification # for information about Standard Algorithm Names. Matching is # performed using a case-insensitive exact matching rule. For Cipher service, @@ -805,18 +808,28 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # unsupported services at the time of checking, an ExceptionInInitializerError # with a cause of IllegalArgumentException will be thrown. # -# Note: The restriction is applied in the various getInstance(...) methods -# of the supported Service classes, i.e. Cipher, KeyStore, MessageDigest, -# and Signature. If the algorithm is disabled, a NoSuchAlgorithmException will -# be thrown by the getInstance methods of Cipher, MessageDigest, and Signature -# and a KeyStoreException by the getInstance methods of KeyStore. +# Note: The jdk.crypto.disabledAlgorithms property is enforced in the various +# getInstance(...) methods of the supported Service classes, i.e. Cipher, +# KeyStore, MessageDigest, and Signature. If the algorithm is disabled, a +# NoSuchAlgorithmException will be thrown by the getInstance methods of +# Cipher, MessageDigest, and Signature and a KeyStoreException by the +# getInstance methods of KeyStore. # -# Note: This property is currently used by the JDK Reference implementation. -# It is not guaranteed to be examined and used by other implementations. +# Note: The jdk.crypto.legacyAlgorithms property is checked in the +# getInstance(...) methods of the supported Service classes, i.e. Cipher, +# KeyStore, MessageDigest, and Signature. If the algorithm is considered legacy, the +# JDK emits a warning at runtime when the algorithm is requested. +# This warning is shown once per caller for each legacy algorithm. +# If the algorithm is also disabled, the warning will not be shown. +# +# Note: These properties are currently used by the JDK Reference implementation. +# They are not guaranteed to be examined and used by other implementations. # # Example: # jdk.crypto.disabledAlgorithms=Cipher.RSA/ECB/PKCS1Padding, MessageDigest.MD2 +# jdk.crypto.legacyAlgorithms=Cipher.RSA/ECB/PKCS1Padding, MessageDigest.MD2 # +#jdk.crypto.legacyAlgorithms= #jdk.crypto.disabledAlgorithms= # diff --git a/src/java.base/share/data/cacerts/luxtrustglobalrootca b/src/java.base/share/data/cacerts/luxtrustglobalrootca deleted file mode 100644 index 7fb3d818f80..00000000000 --- a/src/java.base/share/data/cacerts/luxtrustglobalrootca +++ /dev/null @@ -1,28 +0,0 @@ -Owner: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU -Issuer: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU -Serial number: bb8 -Valid from: Thu Mar 17 09:51:37 GMT 2011 until: Wed Mar 17 09:51:37 GMT 2021 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDZDCCAkygAwIBAgICC7gwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCTFUx -FjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0IEdsb2Jh -bCBSb290MB4XDTExMDMxNzA5NTEzN1oXDTIxMDMxNzA5NTEzN1owRDELMAkGA1UE -BhMCTFUxFjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0 -IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsn+n -QPAiygz267Hxyw6VV0B1r6A/Ps7sqjJX5hmxZ0OYWmt8s7j6eJyqpoSyYBuAQc5j -zR8XCJmk9e8+EsdMsFeaXHhAePxFjdqRZ9w6Ubltc+a3OY52OrQfBfVpVfmTz3iI -Sr6qm9d7R1tGBEyCFqY19vx039a0r9jitScRdFmiwmYsaArhmIiIPIoFdRTjuK7z -CISbasE/MRivJ6VLm6T9eTHemD0OYcqHmMH4ijCc+j4z1aXEAwfh95Z0GAAnOCfR -K6qq4UFFi2/xJcLcopeVx0IUM115hCNq52XAV6DYXaljAeew5Ivo+MVjuOVsdJA9 -x3f8K7p56aTGEnin/wIDAQABo2AwXjAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAfBgNVHSMEGDAWgBQXFYWJCS8kh28/HRvk8pZ5g0gTzjAdBgNVHQ4EFgQU -FxWFiQkvJIdvPx0b5PKWeYNIE84wDQYJKoZIhvcNAQELBQADggEBAFrwHNDUUM9B -fua4nX3DcNBeNv9ujnov3kgR1TQuPLdFwlQlp+HBHjeDtpSutkVIA+qVvuucarQ3 -XB8u02uCgUNbCj8RVWOs+nwIAjegPDkEM/6XMshS5dklTbDG7mgfcKpzzlcD3H0K -DTPy0lrfCmw7zBFRlxqkIaKFNQLXgCLShLL4wKpov9XrqsMLq6F8K/f1O4fhVFfs -BSTveUJO84ton+Ruy4KZycwq3FPCH3CDqyEPVrRI/98HIrOM+R2mBN8tAza53W/+ -MYhm/2xtRDSvCHc+JtJy9LtHVpM8mGPhM7uZI5K1g3noHZ9nrWLWidb2/CfeMifL -hNp3hSGhEiE= ------END CERTIFICATE----- diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index ef99084018d..7f4c10c5a95 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -483,7 +483,7 @@ the JVM. without any warnings. - `warn`: This mode is identical to `allow` except that a warning message is - issued for the first illegal final field mutation performaed in a module. + issued for the first illegal final field mutation performed in a module. This mode is the default for the current JDK but will change in a future release. @@ -1215,9 +1215,11 @@ These `java` options control the runtime behavior of the Java HotSpot VM. be replaced with `[REDACTED]`. The option `redact-argument` is best-effort and applies only to command-line arguments in the `jdk.JVMInformation` event and to the `java.command` system property in the - `jdk.InitialSystemProperty` event. Other events, such as `jdk.ProcessStart` - (child processes), are not redacted. Use `-XX:FlightRecorderOptions:help` - to see the default filters used by the `redact-argument` option. + `jdk.InitialSystemProperty` event, and to matching command-line argument + text in the values of `jdk.InitialEnvironmentVariable` events. Other + events, such as `jdk.ProcessStart` (child processes), are not redacted. + Use `-XX:FlightRecorderOptions:help` to see the default filters used by + the `redact-argument` option. `redact-key=`key-filter : Replace the value of environment variables and system properties @@ -1568,14 +1570,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is similar to `-Xss`. -[`-XX:+UseCompactObjectHeaders`]{#-XX__UseCompactObjectHeaders} -: Enables compact object headers. By default, this option is disabled. - Enabling this option reduces memory footprint in the Java heap by - 4 bytes per object (on average) and often improves performance. +[`-XX:-UseCompactObjectHeaders`]{#-XX__UseCompactObjectHeaders} +: Disables compact object headers. By default, this option is enabled and + compact object headers are used. Using compact object headers reduces + memory footprint in the Java heap by 4 bytes per object (on average) and + often improves performance. - The feature remains disabled by default while it continues to be evaluated. - In a future release it is expected to be enabled by default, and - eventually will be the only mode of operation. + This option can be used if performance regressions are suspected. In a future + release compact object headers is expected to become the only mode of operation. [`-XX:-UseCompressedOops`]{#-XX__UseCompressedOops} : Disables the use of compressed pointers. By default, this option is @@ -2268,6 +2270,23 @@ performed by the Java HotSpot VM. These `java` options provide the ability to gather system information and perform extensive debugging. +[`-XX:AltTempDir=`]{#-XX_AltTempDir}*/path* +: **Linux-only:** On Linux, the usual directory to use for temporary files is `/tmp`. In some secure container + environments however, `/tmp` is made read-only and so is unusable by the VM for its temporary files. To accommodate + this uncommon circumstance the `-XX:AltTempDir` flag can be used to tell the VM to use a different temporary directory. + + It is important to note that this setting controls not only where the VM places its own temporary files, but also the location + it will look for the special files used by other VMs as part of the attach protocol for tools like `jcmd` and `jstack`. That + means that both VMs must use the same setting of this flag. For example, if you start a target VM with + `java -XX:AltTempDir=/scratch/vmTmp` then you must run e.g. `jcmd -J-XX:AltTempDir=/scratch/vmTmp` to interact with that target VM. + + The directory path must of course be writable and accessible to both the target and tool VM, so the simplest arrangement + is to always run both in the same container. + + The value for `AltTempDir` must be an absolute directory path starting with `/`. The length of the `AltTempDir` path should be + fairly small (less than approximately 80 characters) if it is to be used with the attach protocol due to path length limits + for socket files. + [`-XX:+DisableAttachMechanism`]{#-XX__DisableAttachMechanism} : Disables the mechanism that lets tools attach to the JVM. By default, this option is disabled, meaning that the attach mechanism is enabled and you diff --git a/src/java.base/share/man/keytool.md b/src/java.base/share/man/keytool.md index 1d70bd2f5f8..faa2ff563a1 100644 --- a/src/java.base/share/man/keytool.md +++ b/src/java.base/share/man/keytool.md @@ -1191,14 +1191,14 @@ These options can appear for all commands operating on a keystore: [`-keystore`]{#option-keystore} *keystore* : The keystore location. - If the JKS `storetype` is used and a keystore file doesn't yet exist, then - certain `keytool` commands can result in a new keystore file being created. - For example, if `keytool -genkeypair` is called and the `-keystore` option - isn't specified, the default keystore file named `.keystore` is created in - the user's home directory if it doesn't already exist. Similarly, if the - `-keystore ks_file` option is specified but `ks_file` doesn't exist, then - it is created. For more information on the JKS `storetype`, see the - **KeyStore Implementation** section in **KeyStore aliases**. + If a keystore file doesn't yet exist, then certain `keytool` commands can + result in a new keystore file being created. For example, if + `keytool -genkeypair` is called and the `-keystore` option isn't specified, + the default keystore file named `.keystore` is created in the user's home + directory if it doesn't already exist. Similarly, if the `-keystore ks_file` + option is specified but `ks_file` doesn't exist, then it is created. For + more information on keystore types and implementations, see the + **KeyStore implementation** section in [Terms]. Note that the input stream from the `-keystore` option is passed to the `KeyStore.load` method. If `NONE` is specified as the URL, then a null @@ -1766,11 +1766,11 @@ keystore, then it prompts you for a password. If it detects alias duplication, then it asks you for a new alias, and you can specify a new alias or simply allow the `keytool` command to overwrite the existing one. -For example, import entries from a typical JKS type keystore `key.jks` into a -PKCS \#11 type hardware-based keystore, by entering the following command: +For example, import entries from a typical PKCS12 type keystore `key.p12` into +a PKCS \#11 type hardware-based keystore, by entering the following command: -> `keytool -importkeystore -srckeystore key.jks -destkeystore NONE - -srcstoretype JKS -deststoretype PKCS11 -srcstorepass` *password* +> `keytool -importkeystore -srckeystore key.p12 -destkeystore NONE + -srcstoretype PKCS12 -deststoretype PKCS11 -srcstorepass` *password* `-deststorepass` *password* The `importkeystore` command can also be used to import a single entry from a @@ -1780,8 +1780,8 @@ import. With the `-srcalias` option specified, you can also specify the destination alias name, protection password for a secret or private key, and the destination protection password you want as follows: -> `keytool -importkeystore -srckeystore key.jks -destkeystore NONE - -srcstoretype JKS -deststoretype PKCS11 -srcstorepass` *password* +> `keytool -importkeystore -srckeystore key.p12 -destkeystore NONE + -srcstoretype PKCS12 -deststoretype PKCS11 -srcstorepass` *password* `-deststorepass` *password* `-srcalias myprivatekey -destalias myoldprivatekey -srckeypass` *password* `-destkeypass` *password* `-noprompt` @@ -1800,22 +1800,22 @@ certificates for three entities: Ensure that you store all the certificates in the same keystore. ``` -keytool -genkeypair -keystore root.jks -alias root -ext bc:c -keyalg rsa -keytool -genkeypair -keystore ca.jks -alias ca -ext bc:c -keyalg rsa -keytool -genkeypair -keystore server.jks -alias server -keyalg rsa +keytool -genkeypair -keystore root.p12 -alias root -ext bc:c -keyalg rsa +keytool -genkeypair -keystore ca.p12 -alias ca -ext bc:c -keyalg rsa +keytool -genkeypair -keystore server.p12 -alias server -keyalg rsa -keytool -keystore root.jks -alias root -exportcert -rfc > root.pem +keytool -keystore root.p12 -alias root -exportcert -rfc > root.pem -keytool -storepass password -keystore ca.jks -certreq -alias ca | - keytool -storepass password -keystore root.jks +keytool -storepass password -keystore ca.p12 -certreq -alias ca | + keytool -storepass password -keystore root.p12 -gencert -alias root -ext BC=0 -rfc > ca.pem -keytool -keystore ca.jks -importcert -alias ca -file ca.pem +keytool -keystore ca.p12 -importcert -alias ca -file ca.pem -keytool -storepass password -keystore server.jks -certreq -alias server | - keytool -storepass password -keystore ca.jks -gencert -alias ca +keytool -storepass password -keystore server.p12 -certreq -alias server | + keytool -storepass password -keystore ca.p12 -gencert -alias ca -ext ku:c=dig,kE -rfc > server.pem cat root.pem ca.pem server.pem | - keytool -keystore server.jks -importcert -alias server + keytool -keystore server.p12 -importcert -alias server ``` @@ -1886,11 +1886,7 @@ Keystore implementation is a cross platform keystore based on the RSA PKCS12 Personal Information Exchange Syntax Standard. This standard is primarily meant for storing or transporting a user's private keys, certificates, and miscellaneous - secrets. There is another built-in implementation, provided by Oracle. It - implements the keystore as a file with a proprietary keystore type (format) - named `JKS`. It protects each private key with its individual password, and - also protects the integrity of the entire keystore with a (possibly - different) password. + secrets. Keystore implementations are provider-based. More specifically, the application interfaces supplied by `KeyStore` are implemented in terms of a @@ -1946,16 +1942,12 @@ Keystore implementation > `keystore.type=pkcs12` To have the tools utilize a keystore implementation other than the default, - you can change that line to specify a different keystore type. For example, - if you want to use the Oracle's `jks` keystore implementation, then change - the line to the following: - - > `keystore.type=jks` + you can change that line to specify a different keystore type. **Note:** - Case doesn't matter in keystore type designations. For example, `JKS` would - be considered the same as `jks`. + Case doesn't matter in keystore type designations. For example, `PKCS12` + would be considered the same as `pkcs12`. Certificate : A certificate (or public-key certificate) is a digitally signed statement @@ -2157,9 +2149,9 @@ cacerts Certificates File The `cacerts` file represents a system-wide keystore with CA certificates. System administrators can configure and manage that file with the `keytool` - command by specifying `jks` as the keystore type. The `cacerts` keystore - file ships with a default set of root CA certificates. For Linux, macOS, and - Windows, you can list the default certificates with the following command: + command. The `cacerts` keystore file ships with a default set of root CA + certificates. For Linux, macOS, and Windows, you can list the default + certificates with the following command: > `keytool -list -cacerts` diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index e6aebead212..c0cc4ee33e2 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -3705,7 +3705,7 @@ CCerror (context_type *context, char *format, ...) static void CCout_of_memory(context_type *context) { - int n = print_CCerror_info(context); + print_CCerror_info(context); context->err_code = CC_OutOfMemory; longjmp(context->jump_buffer, 1); } diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java index 2d72aeb2ee9..ed28e1a1fe7 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -555,9 +555,10 @@ class UnixNativeDispatcher { /** * Capabilities */ - private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls - private static final int SUPPORTS_XATTR = 1 << 3; - private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features + private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls + private static final int SUPPORTS_FCHMODAT_NOFOLLOW = 1 << 2; + private static final int SUPPORTS_XATTR = 1 << 3; + private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features private static final int capabilities; /** @@ -585,9 +586,8 @@ class UnixNativeDispatcher { * Supports fchmodat with AT_SYMLINK_NOFOLLOW flag */ static boolean fchmodatNoFollowSupported() { - return fchmodatNoFollowSupported0(); + return (capabilities & SUPPORTS_FCHMODAT_NOFOLLOW) != 0; } - private static native boolean fchmodatNoFollowSupported0(); private static native int init(); static { diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 2f163cf27f1..709617333d9 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -41,22 +41,11 @@ #include "TimeZone_md.h" #include "path_util.h" -#define fileopen fopen -#define filegets fgets -#define fileclose fclose - -#if defined(__linux__) || defined(_ALLBSD_SOURCE) +#if defined(__linux__) || defined(MACOSX) static const char *ZONEINFO_DIR = "/usr/share/zoneinfo"; static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime"; -#else -static const char *SYS_INIT_FILE = "/etc/default/init"; -static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo"; -static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime"; -#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */ - static const char popularZones[][4] = {"UTC", "GMT"}; -#if defined(__linux__) || defined(MACOSX) static char *isFileIdentical(char* buf, size_t size, char *pathname); /* @@ -121,7 +110,7 @@ getPathName(const char *dir, const char *name) { /* * Scans the specified directory and its subdirectories to find a * zoneinfo file which has the same content as /etc/localtime on Linux - * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'. + * given in 'buf'. * If file is symbolic link, then the contents it points to are in buf. * Returns a zone ID if found, otherwise, NULL is returned. */ @@ -352,33 +341,15 @@ getPlatformTimeZoneID() } static char * -mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { +getJavaTimezoneFromPlatform(const char *tz_buf, size_t tz_len, const char *mapfilename) { 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); - goto tzerr; + return NULL; } while (fgets(line, sizeof(line), tzmapf) != NULL) { @@ -431,10 +402,58 @@ mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { 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); + if (tz_buf == NULL) { + jio_fprintf(stderr, "Failed to allocate timezone buffer\n"); + goto tzerr; + } + 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); + if (tz_buf == NULL) { + jio_fprintf(stderr, "Failed to allocate timezone buffer\n"); + goto tzerr; + } + 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); } @@ -445,7 +464,7 @@ tzerr: return javatz; } -#endif /* defined(_AIX) */ +#endif /* defined(__linux__) || defined(MACOSX) || defined(_AIX) */ /* * findJavaTZ_md() maps platform time zone ID to Java time zone ID @@ -512,7 +531,6 @@ char * getGMTOffsetID() { char buf[32]; - char offset[6]; struct tm localtm; time_t clock = time(NULL); if (localtime_r(&clock, &localtm) == NULL) { @@ -546,6 +564,7 @@ getGMTOffsetID() snprintf(buf, sizeof(buf), (const char *)"GMT%c%02.2d:%02.2d", gmt_off < 0 ? '-' : '+' , abs(gmt_off / 60), gmt_off % 60); #else + char offset[6]; if (strftime(offset, 6, "%z", &localtm) != 5) { return strdup("GMT"); } diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 4b5cfabebfb..aba16118988 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -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 @@ -388,17 +388,16 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif - return capabilities; -} - -JNIEXPORT jboolean JNICALL -Java_sun_nio_fs_UnixNativeDispatcher_fchmodatNoFollowSupported0(JNIEnv* env, jclass this) { #if defined(__linux__) - // Linux recognizes but does not support the AT_SYMLINK_NOFOLLOW flag - return JNI_FALSE; + // Linux 6.6+ supports AT_SYMLINK_NOFOLLOW. glibc 2.32+ also provides emulation for older kernels. + if (fchmodat(AT_FDCWD, "", 0, AT_SYMLINK_NOFOLLOW) == 0 || errno != ENOTSUP) { + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; + } #else - return JNI_TRUE; + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; #endif + + return capabilities; } JNIEXPORT jbyteArray JNICALL diff --git a/src/java.base/windows/native/libjli/java_md.c b/src/java.base/windows/native/libjli/java_md.c index bf1cb0c0db5..4382eceed7e 100644 --- a/src/java.base/windows/native/libjli/java_md.c +++ b/src/java.base/windows/native/libjli/java_md.c @@ -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 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "java.h" @@ -472,30 +473,24 @@ jlong CurrentTimeMicros() return (jlong)(count.QuadPart * 1000 * 1000 / counterFrequency.QuadPart); } -static errno_t convert_to_unicode(const char* path, const wchar_t* prefix, wchar_t** wpath) { - int unicode_path_len; - size_t prefix_len, wpath_len; - +static errno_t convert_to_unicode(const char* path, wchar_t** wpath) { /* * Get required buffer size to convert to Unicode. * The return value includes the terminating null character. */ - unicode_path_len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, - path, -1, NULL, 0); + int unicode_path_len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, + path, -1, NULL, 0); if (unicode_path_len == 0) { return EINVAL; } - prefix_len = wcslen(prefix); - wpath_len = prefix_len + unicode_path_len; - *wpath = (wchar_t*)JLI_MemAlloc(wpath_len * sizeof(wchar_t)); + *wpath = (wchar_t*)JLI_MemAlloc(unicode_path_len * sizeof(wchar_t)); if (*wpath == NULL) { return ENOMEM; } - wcsncpy(*wpath, prefix, prefix_len); if (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, - path, -1, &((*wpath)[prefix_len]), (int)wpath_len) == 0) { + path, -1, *wpath, unicode_path_len) == 0) { JLI_MemFree(*wpath); *wpath = NULL; return EINVAL; @@ -504,45 +499,224 @@ static errno_t convert_to_unicode(const char* path, const wchar_t* prefix, wchar return ERROR_SUCCESS; } -/* taken from hotspot and slightly adjusted for jli lib; - * creates a UNC/ELP path from input 'path' - * the return buffer is allocated in C heap and needs to be freed using - * JLI_MemFree by the caller. - */ -static wchar_t* create_unc_path(const char* path, errno_t* err) { - wchar_t* wpath = NULL; - if (path[0] == '\\' && path[1] == '\\') { - if (path[2] == '?' && path[3] == '\\') { - /* if it already has a \\?\ don't do the prefix */ - *err = convert_to_unicode(path, L"", &wpath); +static errno_t get_full_path(LPCWSTR unicode_path, LPWSTR stack_buf, + DWORD stack_buf_len, LPWSTR* full_path, + int* needs_free) { + DWORD full_path_len = GetFullPathNameW(unicode_path, stack_buf_len, stack_buf, NULL); + if (full_path_len == 0) { + return EINVAL; + } + + if (full_path_len < stack_buf_len) { + *full_path = stack_buf; + *needs_free = 0; + return ERROR_SUCCESS; + } + + *full_path = (LPWSTR)JLI_MemAlloc(full_path_len * sizeof(WCHAR)); + if (*full_path == NULL) { + return ENOMEM; + } + + if (GetFullPathNameW(unicode_path, full_path_len, *full_path, NULL) == 0) { + JLI_MemFree(*full_path); + *full_path = NULL; + return EINVAL; + } + + *needs_free = 1; + return ERROR_SUCCESS; +} + +static void set_path_prefix(const char* buf, const wchar_t** prefix, + int* prefix_off, int* needs_fullpath) { + *prefix_off = 0; + *needs_fullpath = 1; + + if (isalpha((unsigned char)buf[0]) && !IsDBCSLeadByte(buf[0]) + && buf[1] == ':' && buf[2] == '\\') { + *prefix = L"\\\\?\\"; + } else if (buf[0] == '\\' && buf[1] == '\\') { + /* + * Paths with \\?\ or \\.\ are already extended-length paths, so + * we do not treat them as UNC. + */ + if ((buf[2] == '?' || buf[2] == '.') && buf[3] == '\\') { + *prefix = L""; + *needs_fullpath = 0; } else { - /* only UNC pathname includes double slashes here */ - *err = convert_to_unicode(path, L"\\\\?\\UNC", &wpath); + *prefix = L"\\\\?\\UNC"; + /* Overwrite the first char with the prefix, so \\share\path becomes + * \\?\UNC\share\path */ + *prefix_off = 1; } } else { - *err = convert_to_unicode(path, L"\\\\?\\", &wpath); + *prefix = L"\\\\?\\"; } - return wpath; +} + +/* Adapted from HotSpot's os::native_path() in os_windows.cpp. */ +static char* native_path(char *path) { + char *src = path, *dst = path, *end = path; + char *colon = NULL; + + /* Assumption: '/', '\\', ':', and drive letters are never lead bytes */ + assert(((!IsDBCSLeadByte('/')) && (!IsDBCSLeadByte('\\')) + && (!IsDBCSLeadByte(':'))) && "Illegal lead byte"); + + /* Check for leading separators */ +#define isfilesep(c) ((c) == '/' || (c) == '\\') + while (isfilesep(*src)) { + src++; + } + + if (isalpha((unsigned char)*src) && !IsDBCSLeadByte(*src) && src[1] == ':') { + /* Remove leading separators if followed by drive specifier. */ + *dst++ = *src++; + colon = dst; + *dst++ = ':'; + src++; + } else { + src = path; + if (isfilesep(src[0]) && isfilesep(src[1])) { + /* UNC pathname: Retain first separator; leave src pointed at + * second separator so that further separators will be collapsed. */ + src = dst = path + 1; + path[0] = '\\'; + } + } + + end = dst; + + /* Remove redundant separators from remainder of path, forcing all + * separators to be '\\' rather than '/'. Also, single byte space + * characters are removed from the end of the path. */ + while (*src != '\0') { + if (isfilesep(*src)) { + *dst++ = '\\'; src++; + while (isfilesep(*src)) src++; + if (*src == '\0') { + end = dst; + if (colon == dst - 2) break; /* "z:\\" */ + if (dst == path + 1) break; /* "\\" */ + if (dst == path + 2 && isfilesep(path[0])) { + break; + } + end = --dst; + break; + } + end = dst; + } else { + if (IsDBCSLeadByte(*src)) { + *dst++ = *src++; + if (*src) *dst++ = *src++; + end = dst; + } else { + char c = *src++; + *dst++ = c; + if (c != ' ') end = dst; + } + } + } + + *end = '\0'; + + /* For "z:", add "." to work around a bug in the C runtime library */ + if (colon == dst - 1) { + path[2] = '.'; + path[3] = '\0'; + } + +#undef isfilesep + + return path; +} + +/* Adapted from HotSpot's wide_abs_unc_path() in os_windows.cpp. */ +static wchar_t* convert_to_absolute_path(const char* path, errno_t* err) { + *err = ERROR_SUCCESS; + if (path == NULL || path[0] == '\0') { + *err = ENOENT; + return NULL; + } + + size_t buf_len = 1 + (strlen(path) < 3 ? 3 : strlen(path)); + char* npath = JLI_MemAlloc(buf_len); + if (npath == NULL) { + *err = ENOMEM; + return NULL; + } + strncpy(npath, path, buf_len); + native_path(npath); + + int prefix_off = 0; + int needs_fullpath = 1; + const wchar_t* prefix = NULL; + set_path_prefix(npath, &prefix, &prefix_off, &needs_fullpath); + + wchar_t* unicode_path = NULL; + *err = convert_to_unicode(npath, &unicode_path); + JLI_MemFree(npath); + if (*err != ERROR_SUCCESS) { + return NULL; + } + + int free_full_path = 0; + wchar_t* full_path = NULL; + WCHAR full_path_buf[MAX_PATH]; + + if (needs_fullpath) { + *err = get_full_path(unicode_path, full_path_buf, MAX_PATH, + &full_path, &free_full_path); + if (*err != ERROR_SUCCESS) { + JLI_MemFree(unicode_path); + return NULL; + } + } else { + full_path = unicode_path; + } + + wchar_t* result = NULL; + size_t prefix_len = wcslen(prefix); + size_t result_len = prefix_len - prefix_off + wcslen(full_path) + 1; + result = (wchar_t*)JLI_MemAlloc(result_len * sizeof(wchar_t)); + if (result == NULL) { + *err = ENOMEM; + } else { + _snwprintf(result, result_len, L"%s%s", prefix, &full_path[prefix_off]); + + /* + * Remove trailing pathsep (not for \\?\:\, since it would make + * it relative) + */ + result_len = wcslen(result); + if ((result_len > 0) && result[result_len - 1] == L'\\' && + !(result_len == 7 && iswalpha(result[4]) && result[5] == L':')) { + result[result_len - 1] = L'\0'; + } + } + + if (free_full_path != 0) { + JLI_MemFree(full_path); + } + + JLI_MemFree(unicode_path); + return result; } int JLI_Open(const char* name, int flags) { int fd; - if (strlen(name) < MAX_PATH) { - fd = _open(name, flags); - } else { - errno_t err = ERROR_SUCCESS; - wchar_t* wpath = create_unc_path(name, &err); - if (err != ERROR_SUCCESS) { - if (wpath != NULL) JLI_MemFree(wpath); - errno = err; - return -1; + errno_t err = ERROR_SUCCESS; + wchar_t* wpath = convert_to_absolute_path(name, &err); + if (err != ERROR_SUCCESS) { + errno = err; + if (wpath != NULL) { + JLI_MemFree(wpath); } - fd = _wopen(wpath, flags); - if (fd == -1) { - errno = GetLastError(); - } - JLI_MemFree(wpath); + return -1; } + fd = _wopen(wpath, flags); + JLI_MemFree(wpath); return fd; } diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitorPreview.java index c4257b2e6f1..70a78527b2d 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitorPreview.java @@ -51,7 +51,7 @@ import javax.annotation.processing.ProcessingEnvironment; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public abstract class AbstractAnnotationValueVisitorPreview extends AbstractAnnotationValueVisitor14 { /** diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitorPreview.java index 96651948d45..df2135e2f84 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitorPreview.java @@ -54,7 +54,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public abstract class AbstractElementVisitorPreview extends AbstractElementVisitor14 { /** * Constructor for concrete subclasses to call. diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitorPreview.java index 2bac66f862d..c0981dfb811 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitorPreview.java @@ -54,7 +54,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public abstract class AbstractTypeVisitorPreview extends AbstractTypeVisitor14 { /** * Constructor for concrete subclasses to call. diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitorPreview.java index bca2a26ef29..e4e913c60ab 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitorPreview.java @@ -68,7 +68,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public class ElementKindVisitorPreview extends ElementKindVisitor14 { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementScannerPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementScannerPreview.java index 35ab7b49ec4..e6595b1d2d9 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/ElementScannerPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementScannerPreview.java @@ -82,7 +82,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public class ElementScannerPreview extends ElementScanner14 { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitorPreview.java index b43226caea1..aa48e6c50d4 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitorPreview.java @@ -59,7 +59,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public class SimpleAnnotationValueVisitorPreview extends SimpleAnnotationValueVisitor14 { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitorPreview.java index 3d58fb3b040..ab9462f03e7 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitorPreview.java @@ -62,7 +62,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public class SimpleElementVisitorPreview extends SimpleElementVisitor14 { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitorPreview.java index 5b0db838fbb..816bf85aefe 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitorPreview.java @@ -63,7 +63,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public class SimpleTypeVisitorPreview extends SimpleTypeVisitor14 { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitorPreview.java b/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitorPreview.java index b23a27cd113..01691bd92ef 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitorPreview.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitorPreview.java @@ -67,7 +67,7 @@ import static javax.lang.model.SourceVersion.*; * @since 23 */ @SupportedSourceVersion(RELEASE_28) -@PreviewFeature(feature=PreviewFeature.Feature.LANGUAGE_MODEL, reflective=true) +@PreviewFeature(feature=PreviewFeature.Feature.PREVIEW_SUPPORT, reflective=true) public class TypeKindVisitorPreview extends TypeKindVisitor14 { /** * Constructor for concrete subclasses to call; uses {@code null} 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 4315abe6197..b0794da2e29 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -25,7 +25,12 @@ package sun.lwawt.macosx; +import java.awt.AWTEvent; import java.awt.Component; +import java.awt.Container; +import java.awt.Toolkit; +import java.awt.event.AWTEventListener; +import java.awt.event.ContainerEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Objects; @@ -52,6 +57,16 @@ import sun.awt.AWTAccessor; final class CAccessible extends CFRetainedResource implements Accessible { public static CAccessible getCAccessible(final Accessible a) { + return getCAccessible(a, true); + } + + /** + * @param createIfUndefined if there is not yet a cached CAccessible for + * the given Accessible, then this boolean + * controls whether this method creates a new + * CAccessible or returns null. + */ + private static CAccessible getCAccessible(final Accessible a, final boolean createIfUndefined) { if (a == null) return null; AccessibleContext context = a.getAccessibleContext(); AWTAccessor.AccessibleContextAccessor accessor @@ -60,9 +75,40 @@ final class CAccessible extends CFRetainedResource implements Accessible { if (cachedCAX != null) { return cachedCAX; } - final CAccessible newCAX = new CAccessible(a); - accessor.setNativeAXResource(context, newCAX); - return newCAX; + if (createIfUndefined) { + final CAccessible newCAX = new CAccessible(a); + accessor.setNativeAXResource(context, newCAX); + return newCAX; + } + return null; + } + + static { + // Call CAccessible.dispose() as objects are removed from the AWT hierarchy. + AWTEventListener componentRemovedListener = new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent event) { + if (event.getID() == ContainerEvent.COMPONENT_REMOVED) { + ContainerEvent containerEvent = (ContainerEvent) event; + disposeRecursively(containerEvent.getChild()); + } + } + + private void disposeRecursively(Component c) { + if (c instanceof Container container) { + for (Component child : container.getComponents()) { + disposeRecursively(child); + } + } + if (c instanceof Accessible ax) { + CAccessible ca = getCAccessible(ax, false); + if (ca != null) { + ca.dispose(); + } + } + } + }; + Toolkit.getDefaultToolkit().addAWTEventListener(componentRemovedListener, ContainerEvent.CONTAINER_EVENT_MASK); } private static native void unregisterFromCocoaAXSystem(long ptr); diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java index ea47d9d35e6..962a47aa922 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, 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 @@ -1111,7 +1111,7 @@ public class BMPImageWriter extends ImageWriter implements BMPConstants { incCompImageSize(1); } // Padding to word align absolute encoding - if ( !isEven((int)Math.ceil((absVal-1)/2)) ) { + if (!isEven((absVal - 1) / 2)) { stream.writeByte(0); incCompImageSize(1); } @@ -1247,7 +1247,7 @@ public class BMPImageWriter extends ImageWriter implements BMPConstants { } // Padding - if ( !isEven((int)Math.ceil((absVal+1)/2)) ) { + if (!isEven((absVal + 2) / 2)) { stream.writeByte(0); incCompImageSize(1); } diff --git a/src/java.desktop/share/classes/java/awt/image/Raster.java b/src/java.desktop/share/classes/java/awt/image/Raster.java index 8f35d5819ab..6ae4214b702 100644 --- a/src/java.desktop/share/classes/java/awt/image/Raster.java +++ b/src/java.desktop/share/classes/java/awt/image/Raster.java @@ -308,6 +308,13 @@ public class Raster { if (bandOffsets == null) { throw new NullPointerException("bandOffsets is null"); } + for (int i = 0; i < bandOffsets.length; i++) { + int off = bandOffsets[i]; + if ((off > pixelStride) || (off > scanlineStride)) { + throw new IllegalArgumentException("Band offset " + off + " is too large for stride"); + } + } + lsz = (long)w * pixelStride; if (lsz > scanlineStride) { throw new IllegalArgumentException("w * pixelStride is too large"); @@ -803,6 +810,21 @@ public class Raster { if (dataBuffer == null) { throw new NullPointerException("DataBuffer cannot be null"); } + if (pixelStride < 0) { + throw new IllegalArgumentException("pixelStride is < 0"); + } + if (scanlineStride < 0) { + throw new IllegalArgumentException("scanlineStride is < 0"); + } + if (bandOffsets == null) { + throw new NullPointerException("bandOffsets is null"); + } + for (int i = 0; i < bandOffsets.length; i++) { + int off = bandOffsets[i]; + if ((off > pixelStride) || (off > scanlineStride)) { + throw new IllegalArgumentException("Band offset " + off + " is too large for stride"); + } + } if (location == null) { location = new Point(0, 0); @@ -914,6 +936,14 @@ public class Raster { "bankIndices.length != bandOffsets.length"); } + int numBanks = dataBuffer.getNumBanks(); + for (int i = 0; i < bands; i++) { + if (bankIndices[i] >= numBanks) { + throw new ArrayIndexOutOfBoundsException("Bank[" + i + "] == " + bankIndices[i] + + " and there are only " + numBanks + " banks."); + } + } + if (location == null) { location = new Point(0,0); } else { diff --git a/src/java.desktop/share/classes/javax/print/DocFlavor.java b/src/java.desktop/share/classes/javax/print/DocFlavor.java index 9a5781cc605..171f4b682ac 100644 --- a/src/java.desktop/share/classes/javax/print/DocFlavor.java +++ b/src/java.desktop/share/classes/javax/print/DocFlavor.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 @@ -511,6 +511,7 @@ public class DocFlavor implements Serializable, Cloneable { * value is enclosed in quotes. A "class=" parameter is appended to * the MIME type string to indicate the representation class name. */ + @Override public String toString() { return getStringValue(); } @@ -518,6 +519,7 @@ public class DocFlavor implements Serializable, Cloneable { /** * Returns a hash code for this doc flavor object. */ + @Override public int hashCode() { return getStringValue().hashCode(); } @@ -539,6 +541,7 @@ public class DocFlavor implements Serializable, Cloneable { * @return {@code true} if this doc flavor object equals {@code obj}, * {@code false} otherwise */ + @Override public boolean equals(Object obj) { return obj instanceof DocFlavor other && getStringValue().equals(other.getStringValue()); diff --git a/src/java.desktop/share/classes/javax/print/MimeType.java b/src/java.desktop/share/classes/javax/print/MimeType.java index 9c6aa0e9ac4..2091ed5bb5c 100644 --- a/src/java.desktop/share/classes/javax/print/MimeType.java +++ b/src/java.desktop/share/classes/javax/print/MimeType.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 @@ -135,20 +135,25 @@ class MimeType implements Serializable, Cloneable { public ParameterMapEntry(int theIndex) { myIndex = theIndex; } + @Override public String getKey(){ return myPieces[myIndex]; } + @Override public String getValue(){ return myPieces[myIndex+1]; } + @Override public String setValue (String value) { throw new UnsupportedOperationException(); } + @Override public boolean equals(Object o) { return o instanceof Map.Entry entry && getKey().equals(entry.getKey()) && getValue().equals(entry.getValue()); } + @Override public int hashCode() { return getKey().hashCode() ^ getValue().hashCode(); } @@ -163,9 +168,11 @@ class MimeType implements Serializable, Cloneable { * The current index of the iterator. */ private int myIndex = 2; + @Override public boolean hasNext() { return myIndex < myPieces.length; } + @Override public Map.Entry next() { if (hasNext()) { ParameterMapEntry result = new ParameterMapEntry (myIndex); @@ -175,6 +182,7 @@ class MimeType implements Serializable, Cloneable { throw new NoSuchElementException(); } } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -184,9 +192,11 @@ class MimeType implements Serializable, Cloneable { * Parameter map entry set. */ private class ParameterMapEntrySet extends AbstractSet> { + @Override public Iterator> iterator() { return new ParameterMapEntrySetIterator(); } + @Override public int size() { return (myPieces.length - 2) / 2; } @@ -196,6 +206,7 @@ class MimeType implements Serializable, Cloneable { * Parameter map. */ private class ParameterMap extends AbstractMap { + @Override public Set> entrySet() { if (myEntrySet == null) { myEntrySet = new ParameterMapEntrySet(); @@ -266,6 +277,7 @@ class MimeType implements Serializable, Cloneable { * @return MIME type string based on the canonical form. Each parameter * value is enclosed in quotes. */ + @Override public String toString() { return getStringValue(); } @@ -273,6 +285,7 @@ class MimeType implements Serializable, Cloneable { /** * Returns a hash code for this MIME type object. */ + @Override public int hashCode() { return getStringValue().hashCode(); } @@ -292,6 +305,7 @@ class MimeType implements Serializable, Cloneable { * @return {@code true} if this MIME type object equals {@code obj}, * {@code false} otherwise */ + @Override public boolean equals (Object obj) { return obj instanceof MimeType mimeType && getStringValue().equals(mimeType.getStringValue()); diff --git a/src/java.desktop/share/classes/javax/print/PrintService.java b/src/java.desktop/share/classes/javax/print/PrintService.java index 733abfcd628..a1ef07ee76b 100644 --- a/src/java.desktop/share/classes/javax/print/PrintService.java +++ b/src/java.desktop/share/classes/javax/print/PrintService.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 @@ -425,6 +425,7 @@ public interface PrintService { * @return {@code true} if this service is the same as the obj argument, * {@code false} otherwise */ + @Override public boolean equals(Object obj); /** @@ -433,5 +434,6 @@ public interface PrintService { * * @return hash code of this object */ + @Override public int hashCode(); } diff --git a/src/java.desktop/share/classes/javax/print/SimpleDoc.java b/src/java.desktop/share/classes/javax/print/SimpleDoc.java index 486d16ab466..66e674f0acf 100644 --- a/src/java.desktop/share/classes/javax/print/SimpleDoc.java +++ b/src/java.desktop/share/classes/javax/print/SimpleDoc.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 @@ -131,6 +131,7 @@ public final class SimpleDoc implements Doc { * * @return doc flavor */ + @Override public DocFlavor getDocFlavor() { return flavor; } @@ -151,6 +152,7 @@ public final class SimpleDoc implements Doc { * {@code null} to obtain all attribute values from the job's * attribute set */ + @Override public DocAttributeSet getAttributes() { return attributes; } @@ -168,6 +170,7 @@ public final class SimpleDoc implements Doc { * @throws IOException if the representation class is a stream and there was * an I/O error while constructing the stream */ + @Override public Object getPrintData() throws IOException { return printData; } @@ -193,6 +196,7 @@ public final class SimpleDoc implements Doc { * meet the criteria stated above, {@code null} is returned. * @throws IOException if there was an I/O error while creating the reader */ + @Override public Reader getReaderForText() throws IOException { if (printData instanceof Reader) { @@ -235,6 +239,7 @@ public final class SimpleDoc implements Doc { * @throws IOException if there was an I/O error while creating the input * stream */ + @Override public InputStream getStreamForBytes() throws IOException { if (printData instanceof InputStream) { diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java index a9cc2bba195..d03194a19c5 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java @@ -66,9 +66,7 @@ import javax.print.attribute.PrintRequestAttribute; *

    * The rectangular printable area is defined thus: The (x,y) origin is * positioned at the top-left of the paper in portrait mode regardless of the - * orientation specified in the requesting context. For example a printable area - * for A4 paper in portrait or landscape orientation will have height - * {@literal >} width. + * orientation specified in the requesting context. *

    * A printable area attribute's values are stored internally as integers in * units of micrometers (µm), where 1 micrometer = 10-6 meter = diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java index 57c0d305809..b5ea11cf4c6 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java @@ -36,7 +36,7 @@ import javax.print.attribute.Size2DSyntax; * Class {@code MediaSize} is a two-dimensional size valued printing attribute * class that indicates the dimensions of the medium in a portrait orientation, * with the {@code X} dimension running along the bottom edge and the {@code Y} - * dimension running along the left edge. Thus, the {@code Y} dimension must be + * dimension running along the left edge. For most media, the {@code Y} dimension will be * greater than or equal to the {@code X} dimension. Class {@code MediaSize} * declares many standard media size values, organized into nested classes for * ISO, JIS, North American, engineering, and other media. @@ -77,13 +77,10 @@ public class MediaSize extends Size2DSyntax implements Attribute { * @param units unit conversion factor, e.g. {@code Size2DSyntax.INCH} or * {@code Size2DSyntax.MM} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(float x, float y,int units) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } sizeVector.add(this); } @@ -95,13 +92,10 @@ public class MediaSize extends Size2DSyntax implements Attribute { * @param units unit conversion factor, e.g. {@code Size2DSyntax.INCH} or * {@code Size2DSyntax.MM} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(int x, int y,int units) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } sizeVector.add(this); } @@ -115,13 +109,10 @@ public class MediaSize extends Size2DSyntax implements Attribute { * {@code Size2DSyntax.MM} * @param media a media name to associate with this {@code MediaSize} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(float x, float y,int units, MediaSizeName media) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } if (media != null && mediaMap.get(media) == null) { mediaName = media; mediaMap.put(mediaName, this); @@ -138,13 +129,10 @@ public class MediaSize extends Size2DSyntax implements Attribute { * {@code Size2DSyntax.MM} * @param media a media name to associate with this {@code MediaSize} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(int x, int y,int units, MediaSizeName media) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } if (media != null && mediaMap.get(media) == null) { mediaName = media; mediaMap.put(mediaName, this); diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java index 00e3de4a1de..26885fc1a7d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java @@ -73,16 +73,17 @@ public final class OrientationRequested extends EnumSyntax private static final long serialVersionUID = -4447437289862822276L; /** - * The content will be imaged across the short edge of the medium. + * The content will be imaged across the default orientation of the medium. + * For almost all media this means across the short edge. */ public static final OrientationRequested PORTRAIT = new OrientationRequested(3); /** - * The content will be imaged across the long edge of the medium. Landscape - * is defined to be a rotation of the print-stream page to be imaged by +90 + * Landscape is defined to be a rotation of the print-stream page to be imaged by +90 * degrees with respect to the medium (i.e. anti-clockwise) from the - * portrait orientation. Note: The +90 direction was chosen because + * portrait orientation. For almost all media this means across the long edge. + * Note: The +90 direction was chosen because * simple finishing on the long edge is the same edge whether portrait or * landscape. */ @@ -90,7 +91,7 @@ public final class OrientationRequested extends EnumSyntax LANDSCAPE = new OrientationRequested(4); /** - * The content will be imaged across the long edge of the medium, but in the + * The content will be imaged similarly, but in the * opposite manner from landscape. Reverse-landscape is defined to be a * rotation of the print-stream page to be imaged by -90 degrees with * respect to the medium (i.e. clockwise) from the portrait orientation. @@ -102,7 +103,7 @@ public final class OrientationRequested extends EnumSyntax REVERSE_LANDSCAPE = new OrientationRequested(5); /** - * The content will be imaged across the short edge of the medium, but in + * The content will be imaged similarly to, but in * the opposite manner from portrait. Reverse-portrait is defined to be a * rotation of the print-stream page to be imaged by 180 degrees with * respect to the medium from the portrait orientation. Note: The diff --git a/src/java.desktop/share/classes/javax/print/event/PrintEvent.java b/src/java.desktop/share/classes/javax/print/event/PrintEvent.java index 831e74fbe4f..32f0316a1d1 100644 --- a/src/java.desktop/share/classes/javax/print/event/PrintEvent.java +++ b/src/java.desktop/share/classes/javax/print/event/PrintEvent.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 @@ -53,6 +53,7 @@ public class PrintEvent extends java.util.EventObject { * * @return a message describing the event */ + @Override public String toString() { return ("PrintEvent on " + getSource().toString()); } diff --git a/src/java.desktop/share/classes/javax/print/event/PrintJobAdapter.java b/src/java.desktop/share/classes/javax/print/event/PrintJobAdapter.java index f063fb7a8e0..73b73593acf 100644 --- a/src/java.desktop/share/classes/javax/print/event/PrintJobAdapter.java +++ b/src/java.desktop/share/classes/javax/print/event/PrintJobAdapter.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 @@ -49,6 +49,7 @@ public abstract class PrintJobAdapter implements PrintJobListener { * * @param pje the event being notified */ + @Override public void printDataTransferCompleted(PrintJobEvent pje) { } @@ -57,6 +58,7 @@ public abstract class PrintJobAdapter implements PrintJobListener { * * @param pje the event being notified */ + @Override public void printJobCompleted(PrintJobEvent pje) { } @@ -66,6 +68,7 @@ public abstract class PrintJobAdapter implements PrintJobListener { * * @param pje the event being notified */ + @Override public void printJobFailed(PrintJobEvent pje) { } @@ -74,6 +77,7 @@ public abstract class PrintJobAdapter implements PrintJobListener { * * @param pje the event being notified */ + @Override public void printJobCanceled(PrintJobEvent pje) { } @@ -86,6 +90,7 @@ public abstract class PrintJobAdapter implements PrintJobListener { * * @param pje the event being notified */ + @Override public void printJobNoMoreEvents(PrintJobEvent pje) { } @@ -95,6 +100,7 @@ public abstract class PrintJobAdapter implements PrintJobListener { * * @param pje the event being notified */ + @Override public void printJobRequiresAttention(PrintJobEvent pje) { } } diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index f323842ae49..f6b3895fd8a 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -158,6 +158,42 @@ import sun.swing.SwingAccessor; * expects certain defaults, so that in general * a {@code ComponentUI} provided by one look and feel will not * work with another look and feel. + * + *

    System Look and Feels

    + * + * [The terms "System", "Native" and "Platform" may be used interchangeably in this context]. + *

    + * A System Look And Feel is intended to implement the native Look and Feel of the desktop. + *

    + * There is no requirement for the standard Java Look And Feel to be the default, + * therefore the System Look and Feel may be the default. + *

    + * A desktop may not have a consistent Look and Feel, for example if there are + * multiple platform-native toolkits provided to create applications for the desktop. + * Swing may elect any one of these to be its native Look and Feel. + *

    + * Installation of the native Look and Feel may depend on platform resources being available. + * In the event that required resources are not available, Swing may be unable to install + * the System Look and Feel. + *

    + * Swing's emulation of the native Look and Feel takes precedence over any component-specific + * indication of rendering. + * This means that a native Look and Feel should render in a way that is consistent with the platform, + * even if it contradicts component setting-specific documentation. + * Examples include + *

      + *
    • specified rendering of painted borders may be ignored + *
    • specified rendering of highlighting effects may be ignored + *
    • specified rendering of painted backgrounds and foregrounds may be ignored + *
    • specified rendering of selected vs unselected components may be ignored + *
    • specified rendering of enabled vs disabled components may be ignored + *
    + * These are just examples. Not an exhaustive list. + *

    + * These caveats must not be construed as an excuse to arbitrarily ignore these properties. + * They are intended to support the requirement that the platform Look and Feel be as + * consistent with the native rendering as is practical. + * *

    * Warning: * Serialized objects of this class will not be compatible with diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java index d249bf0bc9d..6f58fd9cdb6 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java @@ -1165,24 +1165,6 @@ public class BasicProgressBarUI extends ProgressBarUI { return repaintInterval; } - /** - * Returns the number of milliseconds per animation cycle. - * This value is meaningful - * only if the progress bar is in indeterminate mode. - * The cycle time is used by the default indeterminate progress bar - * painting code when determining - * how far to move the bouncing box per frame. - * The cycle time is specified by - * the "ProgressBar.cycleTime" UI default - * and adjusted, if necessary, - * by the initIndeterminateDefaults method. - * - * @return the cycle time, in milliseconds - */ - private int getCycleTime() { - return cycleTime; - } - private int initCycleTime() { cycleTime = DefaultLookup.getInt(progressBar, this, "ProgressBar.cycleTime", 3000); diff --git a/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java b/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java index 04772f29858..d5173c181c4 100644 --- a/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java +++ b/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java @@ -206,14 +206,7 @@ class CustomMediaSizeName extends MediaSizeName { if (value.getStandardMedia() == null) { // add this new custom media size name to MediaSize array if ((width > 0.0) && (length > 0.0)) { - try { - new MediaSize(width, length, Size2DSyntax.INCH, value); - } catch (IllegalArgumentException e) { - /* PDF printer in Linux for Ledger paper causes - "IllegalArgumentException: X dimension > Y dimension". - We rotate based on IPP spec. */ - new MediaSize(length, width, Size2DSyntax.INCH, value); - } + new MediaSize(width, length, Size2DSyntax.INCH, value); } } } diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java index 9d09f13e525..8bb7f04420c 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.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 @@ -255,11 +255,4 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { private static void dwmCompositionChanged(boolean enabled) { isDWMCompositionEnabled = enabled; } - - /** - * Used to find out if the OS is Windows Vista or later. - * - * @return {@code true} if the OS is Vista or later, {@code false} otherwise - */ - public static native boolean isVistaOS(); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index 00ad60c8bb3..b288d5beb07 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java @@ -1082,16 +1082,10 @@ public abstract class WComponentPeer extends WObjectPeer */ public boolean isAccelCapable() { if (!isAccelCapable || - !isContainingTopLevelAccelCapable((Component)target)) - { + !isContainingTopLevelAccelCapable((Component)target)) { return false; } - - boolean isTranslucent = - SunToolkit.isContainingTopLevelTranslucent((Component)target); - // D3D/OGL and translucent windows interacted poorly in Windows XP; - // these problems are no longer present in Vista - return !isTranslucent || Win32GraphicsEnvironment.isVistaOS(); + return true; } /** diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java b/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java index 87b1591c0eb..6be79ccaadf 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.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 @@ -312,16 +312,6 @@ final class WPathGraphics extends PathGraphics { } } - private static boolean isXP() { - String osVersion = System.getProperty("os.version"); - if (osVersion != null) { - float version = Float.parseFloat(osVersion); - return version >= 5.1f; - } else { - return false; - } - } - /* In case GDI doesn't handle shaping or BIDI consistently with * 2D's TextLayout, we can detect these cases and redelegate up to * be drawn via TextLayout, which in is rendered as runs of @@ -335,8 +325,7 @@ final class WPathGraphics extends PathGraphics { } else if (!useGDITextLayout) { return true; } else { - if (preferGDITextLayout || - (isXP() && FontUtilities.textLayoutIsCompatible(font))) { + if (preferGDITextLayout || FontUtilities.textLayoutIsCompatible(font)) { return false; } else { return true; diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java index 4ed3e6b7e68..c60a2d6a362 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java @@ -173,15 +173,13 @@ public final class WToolkit extends SunToolkit implements Runnable { } } - private static native String getWindowsVersion(); - static { loadLibraries(); initIDs(); // Print out which version of Windows is running if (log.isLoggable(PlatformLogger.Level.FINE)) { - log.fine("Win version: " + getWindowsVersion()); + log.fine("Win version: " + System.getProperty("os.version")); } } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index 9c1c7665f4b..3c8e4de23cb 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -683,16 +683,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, throw new IllegalArgumentException( "The value of opacity should be in the range [0.0f .. 1.0f]."); } - - if (((this.opacity == 1.0f && opacity < 1.0f) || - (this.opacity < 1.0f && opacity == 1.0f)) && - !Win32GraphicsEnvironment.isVistaOS()) - { - // non-Vista OS: only replace the surface data if opacity status - // changed (see WComponentPeer.isAccelCapable() for more) - replaceSurfaceDataRecursively((Component)getTarget()); - } - this.opacity = opacity; final int maxOpacity = 0xff; @@ -734,14 +724,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, } } - boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS(); - - if (this.isOpaque != isOpaque && !isVistaOS) { - // non-Vista OS: only replace the surface data if the opacity - // status changed (see WComponentPeer.isAccelCapable() for more) - replaceSurfaceDataRecursively(target); - } - synchronized (getStateLock()) { this.isOpaque = isOpaque; setOpaqueImpl(isOpaque); @@ -756,16 +738,14 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, } } - if (isVistaOS) { - // On Vista: setting the window non-opaque makes the window look - // rectangular, though still catching the mouse clicks within - // its shape only. To restore the correct visual appearance - // of the window (i.e. w/ the correct shape) we have to reset - // the shape. - Shape shape = target.getShape(); - if (shape != null) { - target.setShape(shape); - } + // Since Vista: setting the window non-opaque makes the window look + // rectangular, though still catching the mouse clicks within + // its shape only. To restore the correct visual appearance + // of the window (i.e. w/ the correct shape) we have to reset + // the shape. + Shape shape = target.getShape(); + if (shape != null) { + target.setShape(shape); } if (target.isVisible()) { diff --git a/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp b/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp index 189525c39a1..c294303f4c1 100644 --- a/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp +++ b/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.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 @@ -88,8 +88,7 @@ void GetFlagValues(JNIEnv *env, jclass wFlagsClass) } useD3D = d3dEnabled; forceD3DUsage = d3dSet; - setHighDPIAware = - (IS_WINVISTA && GetStaticBoolean(env, wFlagsClass, "setHighDPIAware")); + setHighDPIAware = GetStaticBoolean(env, wFlagsClass, "setHighDPIAware"); JNU_CHECK_EXCEPTION(env); J2dTraceLn(J2D_TRACE_INFO, "WindowsFlags (native):"); diff --git a/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp b/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp index acb3315d1e6..66e9d0c1a89 100644 --- a/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2008, 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 @@ -42,32 +42,18 @@ void ComCtl32Util::InitLibraries() { } WNDPROC ComCtl32Util::SubclassHWND(HWND hwnd, WNDPROC _WindowProc) { - if (IS_WINXP) { - const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc - ::SetWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc, NULL); // _WindowProc is used as subclass ID - return NULL; - } else { - return (WNDPROC)::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_WindowProc); - } + const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc + ::SetWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc, NULL); // _WindowProc is used as subclass ID + return NULL; } void ComCtl32Util::UnsubclassHWND(HWND hwnd, WNDPROC _WindowProc, WNDPROC _DefWindowProc) { - if (IS_WINXP) { - const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc - ::RemoveWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc); // _WindowProc is used as subclass ID - } else { - ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_DefWindowProc); - } + const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc + ::RemoveWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc); // _WindowProc is used as subclass ID } LRESULT ComCtl32Util::DefWindowProc(WNDPROC _DefWindowProc, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (IS_WINXP) { - return ::DefSubclassProc(hwnd, msg, wParam, lParam); - } else if (_DefWindowProc != NULL) { - return ::CallWindowProc(_DefWindowProc, hwnd, msg, wParam, lParam); - } else { - return ::DefWindowProc(hwnd, msg, wParam, lParam); - } + return ::DefSubclassProc(hwnd, msg, wParam, lParam); } LRESULT ComCtl32Util::SharedWindowProc(HWND hwnd, UINT msg, diff --git a/src/java.desktop/windows/native/libawt/windows/awt.h b/src/java.desktop/windows/native/libawt/windows/awt.h index b6289dcae68..8a09d6af994 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt.h +++ b/src/java.desktop/windows/native/libawt/windows/awt.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 @@ -154,16 +154,8 @@ typedef AwtObject* PDATA; JNI_TRUE) /* /NEW JNI */ -/* - * IS_WIN2000 returns TRUE on 2000, XP and Vista - * IS_WINXP returns TRUE on XP and Vista - * IS_WINVISTA returns TRUE on Vista - */ -#define IS_WIN2000 (LOBYTE(LOWORD(::GetVersion())) >= 5) -#define IS_WINXP ((IS_WIN2000 && HIBYTE(LOWORD(::GetVersion())) >= 1) || LOBYTE(LOWORD(::GetVersion())) > 5) -#define IS_WINVISTA (LOBYTE(LOWORD(::GetVersion())) >= 6) #define IS_WIN8 ( \ - (IS_WINVISTA && (HIBYTE(LOWORD(::GetVersion())) >= 2)) || \ + (LOBYTE(LOWORD(::GetVersion())) == 6 && (HIBYTE(LOWORD(::GetVersion())) >= 2)) || \ (LOBYTE(LOWORD(::GetVersion())) > 6)) #define IS_WINVER_ATLEAST(maj, min) \ diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp index 125065c92fe..b7c45463c5a 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, 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 @@ -179,11 +179,7 @@ AwtChoice* AwtChoice::Create(jobject peer, jobject parent) { ::GetClientRect(c->GetHWnd(), &rc); env->SetIntField(target, AwtComponent::widthID, c->ScaleDownX(rc.right)); env->SetIntField(target, AwtComponent::heightID, c->ScaleDownY(rc.bottom)); - - if (IS_WINXP) { - ::SendMessage(c->GetHWnd(), CB_SETMINVISIBLE, (WPARAM) MINIMUM_NUMBER_OF_VISIBLE_ITEMS, 0); - } - + ::SendMessage(c->GetHWnd(), CB_SETMINVISIBLE, (WPARAM) MINIMUM_NUMBER_OF_VISIBLE_ITEMS, 0); env->DeleteLocalRef(dimension); } } catch (...) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp index 502433a13aa..a00938f764f 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, 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 @@ -76,9 +76,7 @@ void AwtDesktopProperties::GetWindowsParameters() { GetOtherParameters(); GetSoundEvents(); GetSystemProperties(); - if (IS_WINXP) { - GetXPStyleProperties(); - } + GetXPStyleProperties(); } void getInvScale(float &invScaleX, float &invScaleY) { @@ -274,20 +272,8 @@ void AwtDesktopProperties::GetNonClientParameters() { // general window properties // NONCLIENTMETRICS ncmetrics; + ncmetrics.cbSize = sizeof(ncmetrics); - // Fix for 6944516: specify correct size for ncmetrics on WIN2K/XP - // Microsoft recommend to subtract the size of 'iPaddedBorderWidth' field - // when running on XP. However this can't be referenced at compile time - // with the older SDK, so there use 'lfMessageFont' plus its size. - if (!IS_WINVISTA) { -#if defined(_MSC_VER) - ncmetrics.cbSize = offsetof(NONCLIENTMETRICS, iPaddedBorderWidth); -#else - ncmetrics.cbSize = offsetof(NONCLIENTMETRICS,lfMessageFont) + sizeof(LOGFONT); -#endif - } else { - ncmetrics.cbSize = sizeof(ncmetrics); - } VERIFY( SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncmetrics.cbSize, &ncmetrics, FALSE) ); float invScaleX; @@ -423,12 +409,8 @@ void CheckFontSmoothingSettings(HWND hWnd) { if (firstTime) { SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0); - if (IS_WINXP) { - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, - &fontSmoothingType, 0); - SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, - &fontSmoothingContrast, 0); - } + SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fontSmoothingType, 0); + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &fontSmoothingContrast, 0); lastFontSmoothing = fontSmoothing; lastFontSmoothingType = fontSmoothingType; lastFontSmoothingContrast = fontSmoothingContrast; @@ -441,28 +423,18 @@ void CheckFontSmoothingSettings(HWND hWnd) { /* no need to check the other settings in this case. */ return; } - if (IS_WINXP) { - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, - &fontSmoothingType, 0); - settingsChanged |= fontSmoothingType != lastFontSmoothingType; - if (!settingsChanged && - fontSmoothingType == FONTSMOOTHING_STANDARD) { - /* No need to check any LCD specific settings */ - return; - } else { - SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, - &fontSmoothingContrast, 0); - settingsChanged |= - fontSmoothingContrast != lastFontSmoothingContrast; - if (fontSmoothingType == FONTSMOOTHING_LCD) { - // Order is a registry entry so more expensive to check.x - subPixelOrder = GetLCDSubPixelOrder(); - settingsChanged |= subPixelOrder != lastSubpixelOrder; - } - } + SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fontSmoothingType, 0); + settingsChanged |= fontSmoothingType != lastFontSmoothingType; + if (!settingsChanged && fontSmoothingType == FONTSMOOTHING_STANDARD) { + /* No need to check any LCD specific settings */ + return; } else { - if (settingsChanged && fontSmoothing == FONTSMOOTHING_ON) { - fontSmoothingType = FONTSMOOTHING_STANDARD; + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &fontSmoothingContrast, 0); + settingsChanged |= fontSmoothingContrast != lastFontSmoothingContrast; + if (fontSmoothingType == FONTSMOOTHING_LCD) { + // Order is a registry entry so more expensive to check.x + subPixelOrder = GetLCDSubPixelOrder(); + settingsChanged |= subPixelOrder != lastSubpixelOrder; } } } @@ -519,13 +491,7 @@ void AwtDesktopProperties::GetColorParameters() { SetColorProperty(TEXT("win.mdi.backgroundColor"), GetSysColor(COLOR_APPWORKSPACE)); SetColorProperty(TEXT("win.menu.backgroundColor"), GetSysColor(COLOR_MENU)); SetColorProperty(TEXT("win.menu.textColor"), GetSysColor(COLOR_MENUTEXT)); - // COLOR_MENUBAR is only defined on WindowsXP. Our binaries are - // built on NT, hence the below ifdef. -#ifndef COLOR_MENUBAR -#define COLOR_MENUBAR 30 -#endif - SetColorProperty(TEXT("win.menubar.backgroundColor"), - GetSysColor(IS_WINXP ? COLOR_MENUBAR : COLOR_MENU)); + SetColorProperty(TEXT("win.menubar.backgroundColor"), GetSysColor(COLOR_MENUBAR)); SetColorProperty(TEXT("win.scrollbar.backgroundColor"), GetSysColor(COLOR_SCROLLBAR)); SetColorProperty(TEXT("win.text.grayedTextColor"), GetSysColor(COLOR_GRAYTEXT)); SetColorProperty(TEXT("win.tooltip.backgroundColor"), GetSysColor(COLOR_INFOBK)); @@ -540,14 +506,9 @@ void AwtDesktopProperties::GetOtherParameters() { SetBooleanProperty(TEXT("win.text.fontSmoothingOn"), GetBooleanParameter(SPI_GETFONTSMOOTHING)); // TODO END - if (IS_WINXP) { - SetIntegerProperty(TEXT("win.text.fontSmoothingType"), - GetIntegerParameter(SPI_GETFONTSMOOTHINGTYPE)); - SetIntegerProperty(TEXT("win.text.fontSmoothingContrast"), - GetIntegerParameter(SPI_GETFONTSMOOTHINGCONTRAST)); - SetIntegerProperty(TEXT("win.text.fontSmoothingOrientation"), - GetLCDSubPixelOrder()); - } + SetIntegerProperty(TEXT("win.text.fontSmoothingType"), GetIntegerParameter(SPI_GETFONTSMOOTHINGTYPE)); + SetIntegerProperty(TEXT("win.text.fontSmoothingContrast"), GetIntegerParameter(SPI_GETFONTSMOOTHINGCONTRAST)); + SetIntegerProperty(TEXT("win.text.fontSmoothingOrientation"), GetLCDSubPixelOrder()); int cxdrag = GetSystemMetrics(SM_CXDRAG); int cydrag = GetSystemMetrics(SM_CYDRAG); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp b/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp index ace140593f6..d1a4fc68d03 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -369,14 +369,8 @@ AwtMenuItem::DrawSelf(DRAWITEMSTRUCT& drawInfo) // Disabled text must be drawn in gray. crText = ::GetSysColor(bEnabled? COLOR_HIGHLIGHTTEXT : COLOR_GRAYTEXT); } else { - // COLOR_MENUBAR is only defined on WindowsXP. Our binaries are - // built on NT, hence the below ifdef. - -#ifndef COLOR_MENUBAR -#define COLOR_MENUBAR 30 -#endif // Set background and text colors for unselected item - if (IS_WINXP && IsTopMenu() && AwtDesktopProperties::IsXPStyle()) { + if (IsTopMenu() && AwtDesktopProperties::IsXPStyle()) { crBack = ::GetSysColor (COLOR_MENUBAR); } else { crBack = ::GetSysColor (COLOR_MENU); @@ -401,9 +395,7 @@ AwtMenuItem::DrawSelf(DRAWITEMSTRUCT& drawInfo) //draw check mark int checkWidth = ::GetSystemMetrics(SM_CXMENUCHECK); // Workaround for CR#6401956 - if (IS_WINVISTA) { - AdjustCheckWidth(checkWidth); - } + AdjustCheckWidth(checkWidth); if (IsCheckbox()) { // means that target is a java.awt.CheckboxMenuItem @@ -564,9 +556,7 @@ void AwtMenuItem::MeasureSelf(HDC hDC, MEASUREITEMSTRUCT& measureInfo) if (!IsTopMenu()) { int checkWidth = ::GetSystemMetrics(SM_CXMENUCHECK); // Workaround for CR#6401956 - if (IS_WINVISTA) { - AdjustCheckWidth(checkWidth); - } + AdjustCheckWidth(checkWidth); measureInfo.itemWidth += checkWidth; // Add in shortcut width, if one exists. diff --git a/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp b/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp index 6dc21c5adda..282bd194f2f 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, 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 @@ -78,7 +78,7 @@ void AwtTextArea::EditSetSel(CHARRANGE &cr) { SendMessage(EM_EXSETSEL, 0, reinterpret_cast(&cr)); SendMessage(EM_HIDESELECTION, TRUE, TRUE); // 6417581: force expected drawing - if (IS_WINVISTA && cr.cpMin == cr.cpMax) { + if (cr.cpMin == cr.cpMax) { ::InvalidateRect(GetHWnd(), NULL, TRUE); } } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp b/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp index 5518ab91145..c1b682ffa46 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, 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 @@ -55,7 +55,7 @@ void AwtTextField::EditSetSel(CHARRANGE &cr) { SendMessage(EM_EXSETSEL, 0, reinterpret_cast(&cr)); // 6417581: force expected drawing - if (IS_WINVISTA && cr.cpMin == cr.cpMax) { + if (cr.cpMin == cr.cpMax) { ::InvalidateRect(GetHWnd(), NULL, TRUE); } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp index a94c96c58c5..c91ff821cba 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp @@ -2848,41 +2848,6 @@ Java_sun_awt_windows_WToolkit_isDynamicLayoutSupportedNative(JNIEnv *env, CATCH_BAD_ALLOC_RET(FALSE); } -/* - * Class: sun_awt_windows_WToolkit - * Method: printWindowsVersion - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL -Java_sun_awt_windows_WToolkit_getWindowsVersion(JNIEnv *env, jclass cls) -{ - TRY; - - WCHAR szVer[128]; - - DWORD version = ::GetVersion(); - swprintf(szVer, 128, L"0x%x = %ld", version, version); - int l = lstrlen(szVer); - - if (IS_WIN2000) { - if (IS_WINXP) { - if (IS_WINVISTA) { - swprintf(szVer + l, 128, L" (Windows Vista)"); - } else { - swprintf(szVer + l, 128, L" (Windows XP)"); - } - } else { - swprintf(szVer + l, 128, L" (Windows 2000)"); - } - } else { - swprintf(szVer + l, 128, L" (Unknown)"); - } - - return JNU_NewStringPlatform(env, szVer); - - CATCH_BAD_ALLOC_RET(NULL); -} - JNIEXPORT void JNICALL Java_sun_awt_windows_WToolkit_showTouchKeyboard(JNIEnv *env, jobject self, jboolean causedByTouchEvent) diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp index cc53f4a3322..5aa7731c8a6 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp @@ -90,20 +90,13 @@ void DWMResetCompositionEnabled() { } /** - * Returns true if dwm composition is enabled, false if it is not applicable - * (if the OS is not Vista) or dwm composition is disabled. + * Returns true if DWM composition is enabled, false if DWM composition is disabled. */ BOOL DWMIsCompositionEnabled() { - // cheaper to check than whether it's vista or not if (dwmIsCompositionEnabled != DWM_COMP_UNDEFINED) { return (BOOL)dwmIsCompositionEnabled; } - if (!IS_WINVISTA) { - dwmIsCompositionEnabled = FALSE; - return FALSE; - } - BOOL bRes = FALSE; try { @@ -337,13 +330,3 @@ Java_sun_awt_Win32GraphicsEnvironment_getYResolution(JNIEnv *env, jobject wge) CATCH_BAD_ALLOC_RET(0); } -/* - * Class: sun_awt_Win32GraphicsEnvironment - * Method: isVistaOS - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_sun_awt_Win32GraphicsEnvironment_isVistaOS - (JNIEnv *env, jclass wgeclass) -{ - return IS_WINVISTA; -} 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 1e0a924f12c..3bbebf5f9d7 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 @@ -1173,8 +1173,14 @@ public final class Connection implements Runnable { tlsHandshakeCompleted.complete(tlsServerCert); } catch (SSLPeerUnverifiedException ex) { CommunicationException ce = new CommunicationException(); - ce.setRootCause(closureReason); - tlsHandshakeCompleted.completeExceptionally(ex); + IOException priorFailure = closureReason; + if (priorFailure != null) { + ce.setRootCause(priorFailure); + ce.addSuppressed(ex); + } else { + ce.setRootCause(ex); + } + tlsHandshakeCompleted.completeExceptionally(ce); } } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java index ddbcce661aa..268705f6c1f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, 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 @@ -102,11 +102,14 @@ public final class MinimalFuture extends CompletableFuture { @Override public boolean cancel(boolean mayInterruptIfRunning) { - boolean result = false; - if (cancelable != null && !isDone()) { - result = cancelable.cancel(mayInterruptIfRunning); + if (!super.cancel(mayInterruptIfRunning)) { + assert isDone(); + return false; } - return super.cancel(mayInterruptIfRunning) || result; + if (cancelable != null) { + cancelable.cancel(mayInterruptIfRunning); + } + return true; } private Cancelable cancelable() { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java index 487a8a186f6..f2991c0738e 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.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 @@ -645,22 +645,15 @@ public sealed class PacketSpaceManager implements PacketSpace return false; } - boolean hasNoDeadline() { - return Deadline.MAX.equals(nextDeadline); - } - // reschedule this task void reschedule() { Deadline deadline = computeNextDeadline(); - Deadline nextDeadline = this.nextDeadline; if (Deadline.MAX.equals(deadline)) { - debug.log("no deadline, don't reschedule"); - } else if (deadline.equals(nextDeadline)) { - debug.log("deadline unchanged, don't reschedule"); - } else { - packetEmitter.reschedule(this, deadline); - debug.log("retransmission task: rescheduled"); + if (debug.on()) debug.log("no deadline, don't reschedule"); + return; } + if (debug.on()) debug.log("retransmission task: rescheduled"); + packetEmitter.reschedule(this, deadline); } @Override @@ -1304,7 +1297,7 @@ public sealed class PacketSpaceManager implements PacketSpace } finally { transferLock.unlock(); } - if (found && packetTransmissionTask.hasNoDeadline()) { + if (found) { packetTransmissionTask.reschedule(); } if (!found) { @@ -1340,9 +1333,7 @@ public sealed class PacketSpaceManager implements PacketSpace return; } addAcknowledgement(pending); - if (packetTransmissionTask.hasNoDeadline()) { - packetTransmissionTask.reschedule(); - } + packetTransmissionTask.reschedule(); } finally { transferLock.unlock(); } 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 3dee814e1f1..18fd7717d6d 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 @@ -1788,9 +1788,10 @@ public abstract sealed class QuicEndpoint implements AutoCloseable if (more > 16) { // the server doesn't seem to take into account our // connection close frame. Just stop responding - updatedDeadline = Deadline.MIN; + updated = updatedDeadline = Deadline.MIN; } else { - updatedDeadline = updated.plusMillis(maxIdleTimeMs); + updated = updatedDeadline = timeSource().instant() + .plusMillis(maxIdleTimeMs); } handleIncoming(source, destConnId, headersType, buffer); } else { @@ -1798,7 +1799,7 @@ public abstract sealed class QuicEndpoint implements AutoCloseable dropIncoming(source, destConnId, headersType, buffer); } - timer().reschedule(this, updatedDeadline); + timer().reschedule(this, updated); } protected void handleIncoming(SocketAddress source, ByteBuffer idbytes, @@ -1821,8 +1822,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable } public final void startTimer() { - deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); - timer().offer(this); + Deadline deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); + timer().reschedule(this, deadline); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java index 830415593cb..bbb88cf1c45 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, 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 @@ -74,6 +74,9 @@ public final class QuicTimerQueue { private volatile Deadline scheduledDeadline = Deadline.MAX; private volatile Deadline returnedDeadline = Deadline.MAX; + // Not volatile: never accessed without holding monitor + private Deadline notifiedDeadline = Deadline.MAX; + /** * Creates a new timer queue with the given notifier. * A notifier is used to notify the timer thread that @@ -113,33 +116,8 @@ public final class QuicTimerQueue { * @param event an event to be scheduled */ public void offer(QuicTimedEvent event) { - if (event instanceof Marker marker) - throw new IllegalArgumentException(marker.name()); - assert QuicTimedEvent.COMPARATOR.compare(event, FLOOR) > 0; - assert QuicTimedEvent.COMPARATOR.compare(event, CEILING) < 0; - Deadline deadline = event.deadline(); - scheduled.add(event); - scheduled(deadline); if (debug.on()) debug.log("QuicTimerQueue: event %s offered", event); - if (notify(deadline)) { - if (debug.on()) debug.log("QuicTimerQueue: event %s will be rescheduled", event); - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - notifier.run(); - } else { - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will not be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - } + reschedule(event, event.deadline()); } /** @@ -181,7 +159,7 @@ public final class QuicTimerQueue { int drained = 0; int dues; synchronized (this) { - scheduledDeadline = Deadline.MAX; + scheduledDeadline = returnedDeadline = notifiedDeadline = Deadline.MAX; } // moved scheduled / rescheduled tasks to due, until // nothing else is due. Then process dues. @@ -347,7 +325,16 @@ public final class QuicTimerQueue { synchronized (this) { if (deadline.isBefore(nextDeadline()) || deadline.isBefore(returnedDeadline)) { - return true; + // notifiedDeadline will be reset to MAX first thing in + // processEventAndReturnNextDeadline; We do not want + // to call the notifier (wake the selector) again if it's + // been already called for a notifiedDeadline <= to deadline; + // On the other hand, if deadline < notifiedDeadline, we + // need to call the notifier to force an additional wakeup + if (deadline.isBefore(notifiedDeadline)) { + notifiedDeadline = deadline; + return true; + } } } return false; diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java index 906bbb88252..806b37c8bb0 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -641,7 +641,7 @@ public class XSLTErrorResources_de extends ListResourceBundle "Vorlagen m\u00FCssen entweder ein \"match\"- oder ein \"name\"-Attribut haben"}, { ER_NO_CLONE_OF_DOCUMENT_FRAG, - "Kein Clone eines Dokumentfragments."}, + "Kein Klon eines Dokumentfragments."}, { ER_CANT_CREATE_ITEM, "Element in Ergebnisbaum kann nicht erstellt werden: {0}"}, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java index 3c53ea08956..9337b13b8fc 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java @@ -223,7 +223,7 @@ public class XMLErrorResources_de extends ListResourceBundle "Fehler: Iterator f\u00FCr Achse {0} nicht implementiert "}, { ER_ITERATOR_CLONE_NOT_SUPPORTED, - "Iteratorclone nicht unterst\u00FCtzt"}, + "Iteratorklon nicht unterst\u00FCtzt"}, { ER_UNKNOWN_AXIS_TYPE, "Unbekannter Achsendurchlauftyp: {0}"}, diff --git a/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp b/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp index fdd7ff524da..7e4f63d2363 100644 --- a/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp +++ b/src/jdk.accessibility/windows/native/jabswitch/jabswitch.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 @@ -53,23 +53,6 @@ static LPCTSTR STR_ACCESSBRIDGE = FILE* origFile; FILE* tempFile; -bool isXP() -{ - static bool isXPFlag = false; - OSVERSIONINFO osvi; - - // Initialize the OSVERSIONINFO structure. - ZeroMemory( &osvi, sizeof( osvi ) ); - osvi.dwOSVersionInfoSize = sizeof( osvi ); - - GetVersionEx( &osvi ); - - if ( osvi.dwMajorVersion == 5 ) // For Windows XP and Windows 2000 - isXPFlag = true; - - return isXPFlag ; -} - void enableJAB() { // Copy lines from orig to temp modifying the line containing // assistive_technologies= @@ -458,16 +441,14 @@ int main(int argc, char* argv[]) { enableWasRequested = true; error = modify(true); if (error == 0) { - if( !isXP() ) - regEnable(); + regEnable(); } } else if (_stricmp(argv[1], "-disable") == 0 || _stricmp(argv[1], "/disable") == 0) { badParams = false; disableWasRequested = true; error = modify(false); if (error == 0) { - if( !isXP() ) - regDisable(); + regDisable(); } } } diff --git a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java index 4a1cd3aba78..ae64c8609ae 100644 --- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java @@ -45,6 +45,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import sun.jvmstat.monitor.MonitoredHost; import sun.jvmstat.monitor.MonitorException; +import sun.jvmstat.PlatformSupport; /* * Linux implementation of HotSpotVirtualMachine @@ -55,12 +56,13 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // .java_pid. and .attach_pid. It is important that this // location is the same for all processes, otherwise the tools // will not be able to find all Hotspot processes. - // Any changes to this needs to be synchronized with HotSpot. - private static final Path TMPDIR = Path.of("/tmp"); + // This calls a Hotspot native method to get a consistent temporary + // directory. + private static final String vmTemp = PlatformSupport.getTemporaryDirectory(); + private static final Path TMPDIR = Path.of(vmTemp); private static final Path PROC = Path.of("/proc"); private static final Path STATUS = Path.of("status"); - private static final Path ROOT_TMP = Path.of("root/tmp"); String socket_path; private OperationProperties props = new OperationProperties(VERSION_1); // updated in ctor @@ -86,6 +88,9 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // Then we attempt to find the socket file again. final File socket_file = findSocketFile(pid, ns_pid); socket_path = socket_file.getPath(); + if (!validateSocketFileLength(socket_file.getPath())) { + throw new AttachNotSupportedException("Socket file path too long: " + socket_path); + } if (!socket_file.exists()) { // Keep canonical version of File, to delete, in case target process ends and /proc link has gone: File f = createAttachFile(pid, ns_pid).getCanonicalFile(); @@ -255,7 +260,8 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { } private String findTargetProcessTmpDirectory(long pid) throws IOException { - final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); + final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve("root") + .resolve(vmTemp.startsWith("/") ? vmTemp.substring(1) : vmTemp); /* We need to handle at least 4 different cases: * 1. Caller and target processes share PID namespace and root @@ -429,6 +435,8 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { static native void write(int fd, byte buf[], int off, int bufLen) throws IOException; + static native boolean validateSocketFileLength(String socketPath); + static { System.loadLibrary("attach"); } diff --git a/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c index fc9af901835..df4fc54cc96 100644 --- a/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c @@ -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 @@ -76,11 +76,15 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - /* strncpy is safe because addr.sun_path was zero-initialized before. */ - strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1); + if (strlen(p) >= sizeof(addr.sun_path)) { + JNU_ThrowIOException(env, "Socket file path too long"); + } else { + /* strncpy is safe because addr.sun_path was zero-initialized before. */ + strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1); - if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { - err = errno; + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + err = errno; + } } if (isCopy) { @@ -256,3 +260,30 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write } while (remaining > 0); } + +/* + * Class: sun_tools_attach_VirtualMachineImpl + * Method: validateSocketFileLength + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_sun_tools_attach_VirtualMachineImpl_validateSocketFileLength + (JNIEnv *env, jclass cls, jstring path) +{ + jboolean isCopy; + const char* p = GetStringPlatformChars(env, path, &isCopy); + if (p == NULL) { + JNU_ThrowIOException(env, "Socket file path is null"); + return JNI_FALSE; + } + + size_t pathLength = strlen(p); + + if (isCopy) { + JNU_ReleaseStringPlatformChars(env, path, p); + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + return pathLength < sizeof(addr.sun_path); +} diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java index af47f9073f9..29018cfe00d 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, 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 @@ -49,7 +49,7 @@ import com.sun.tools.javac.util.Context; * @author Jonathan Gibbons * @since 1.6 */ -public abstract class JavacTask implements CompilationTask { +public abstract class JavacTask implements CompilationTask, AutoCloseable { /** * Constructor for subclasses to call. */ @@ -101,6 +101,19 @@ public abstract class JavacTask implements CompilationTask { */ public abstract Iterable generate() throws IOException; + /** + * Releases any resources opened by this task, either directly or + * indirectly. After this method is called, the task becomes unusable, + * and subsequent calls to its methods may throw an {@code IllegalStateException}. + * Closing a task that has already been closed has no effect. + * + * @throws IOException if an error occurs while releasing resources. + * @throws IllegalStateException if the operation cannot be performed at this time. + * @since 28 + */ + @Override + public abstract void close() throws IOException; + /** * Sets a specified listener to receive notification of events * describing the progress of this compilation task. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java index 8bad3f64c38..c334d491174 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, 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 @@ -109,6 +109,11 @@ public class BasicJavacTask extends JavacTask { throw new IllegalStateException(); } + @Override @DefinedBy(Api.COMPILER_TREE) + public void close() { + throw new IllegalStateException(); + } + @Override @DefinedBy(Api.COMPILER_TREE) public void setTaskListener(TaskListener tl) { MultiTaskListener mtl = MultiTaskListener.instance(context); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java index bc597876778..32fe6bd98ce 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.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 @@ -495,6 +495,11 @@ public class JavacTaskImpl extends BasicJavacTask { return results; } + @Override @DefinedBy(Api.COMPILER_TREE) + public void close() { + cleanup(); + } + public void ensureEntered() { args.allowEmpty(); enter(null); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index be10388cbf3..c0fb23cbb2d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -873,11 +873,10 @@ public class ThisEscapeAnalyzer extends TreeScanner { @Override public void visitAssign(JCAssign tree) { - VarSymbol sym = (VarSymbol)TreeInfo.symbolFor(tree.lhs); scan(tree.lhs); refs.discardExprs(depth); scan(tree.rhs); - if (isParamOrVar(sym)) + if (TreeInfo.symbolFor(tree.lhs) instanceof VarSymbol sym && isParamOrVar(sym)) refs.replaceExprs(depth, ref -> new VarRef(sym, ref)); else refs.discardExprs(depth); // we don't track fields yet diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java index c4573b9a364..7d10b8a45c0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.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 @@ -1952,13 +1952,19 @@ public class Locations { if (isCurrentPlatform(systemJavaHome)) { jrtfs = FileSystems.getFileSystem(jrtURI); } else { + ClassLoader currentLoader = Locations.class.getClassLoader(); try { Map attrMap = Collections.singletonMap("java.home", systemJavaHome.toString()); jrtfs = FileSystems.newFileSystem(jrtURI, attrMap); + // Ensure the file system’s class loader is closed so that + // the ${systemJavaHome}/lib/jrt-fs.jar is not left open. + ClassLoader cl = jrtfs.provider().getClass().getClassLoader(); + if (cl != currentLoader && cl instanceof URLClassLoader urlcl) { + closeables.add(urlcl); + } } catch (ProviderNotFoundException ex) { URL jfsJar = resolveInJavaHomeLib(systemJavaHome, "jrt-fs.jar").toUri().toURL(); - ClassLoader currentLoader = Locations.class.getClassLoader(); URLClassLoader fsLoader = new URLClassLoader(new URL[] {jfsJar}, currentLoader); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 58beee78af2..9a0b75a3aa4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -32,7 +32,6 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -278,12 +277,8 @@ public class Arguments { */ public Set getFileObjects() { if (fileObjects == null) { - fileObjects = new LinkedHashSet<>(); - } - if (files != null) { - JavacFileManager jfm = (JavacFileManager) getFileManager(); - for (JavaFileObject fo: jfm.getJavaFileObjectsFromPaths(files)) - fileObjects.add(fo); + // see Arguments::validate + throw new IllegalStateException("file objects have not been initialized"); } return fileObjects; } @@ -421,6 +416,9 @@ public class Arguments { */ public boolean validate() { JavaFileManager fm = getFileManager(); + if (fileObjects == null) { + fileObjects = new LinkedHashSet<>(); + } if (options.isSet(Option.MODULE)) { if (!fm.hasLocation(StandardLocation.CLASS_OUTPUT)) { log.error(Errors.OutputDirMustBeSpecifiedWithDashMOption); @@ -433,19 +431,10 @@ public class Arguments { Location sourceLoc = fm.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, module); if (sourceLoc == null) { log.error(Errors.ModuleNotFoundInModuleSourcePath(module)); - } else { - Location classLoc = fm.getLocationForModule(StandardLocation.CLASS_OUTPUT, module); - - for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(JavaFileObject.Kind.SOURCE), true)) { - String className = fm.inferBinaryName(sourceLoc, file); - JavaFileObject classFile = fm.getJavaFileForInput(classLoc, className, Kind.CLASS); - - if (classFile == null || classFile.getLastModified() < file.getLastModified()) { - if (fileObjects == null) - fileObjects = new HashSet<>(); - fileObjects.add(file); - } - } + return false; + } + for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(Kind.SOURCE), true)) { + fileObjects.add(file); } } } catch (IOException ex) { @@ -455,6 +444,12 @@ public class Arguments { } } } + if (files != null) { + JavacFileManager jfm = (JavacFileManager) getFileManager(); + for (JavaFileObject fo : jfm.getJavaFileObjectsFromPaths(files)){ + fileObjects.add(fo); + } + } if (isEmpty()) { // It is allowed to compile nothing if just asking for help or version info. 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 11fa3a5aebf..ede75a73824 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 @@ -1547,7 +1547,8 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea } public void visitMethodDef(JCMethodDecl node) { // remove super constructor call that may have been added during attribution: - if (TreeInfo.isConstructor(node) && node.sym != null && node.sym.owner.isEnum() && + if (TreeInfo.isConstructor(node) && node.sym != null && + (node.sym.owner.isEnum() || TreeInfo.isCanonicalConstructor(node)) && node.body != null && node.body.stats.nonEmpty() && TreeInfo.isSuperCall(node.body.stats.head) && node.body.stats.head.pos == node.body.pos) { node.body.stats = node.body.stats.tail; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties index b8fa413adba..36a71fa424f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties @@ -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 @@ -710,7 +710,11 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=Lambda-Body ist weder # 0: list of type compiler.err.incompatible.thrown.types.in.mref=Inkompatible ausgelöste Typen {0} in Funktionsausdruck -compiler.misc.incompatible.arg.types.in.lambda=Inkompatible Parametertypen in Lambda-Ausdruck +# 0: list of type or message segment, 1: list of type or message segment +compiler.misc.incompatible.arg.types.in.lambda=Inkompatible Parametertypen in Lambda-Ausdruck\nErforderlich: {0}\nGefunden: {1} + +# 0: symbol +compiler.misc.wrong.number.args.in.lambda=Falsche Anzahl an Parametern in Lambda-Ausdruck für funktionale Schnittstelle {0} compiler.misc.incompatible.arg.types.in.mref=Inkompatible Parametertypen in Methodenreferenz @@ -1046,6 +1050,19 @@ compiler.err.not.exhaustive=Der Switch-Ausdruck deckt nicht alle möglichen Eing compiler.err.not.exhaustive.statement=Die Switch-Anweisung deckt nicht alle möglichen Eingabewerte ab +compiler.err.not.exhaustive.details=Der Switch-Ausdruck deckt nicht alle möglichen Eingabewerte ab\nFehlende Muster: + +compiler.err.not.exhaustive.statement.details=Die Switch-Anweisung deckt nicht alle möglichen Eingabewerte ab\nFehlende Muster: + +# 0: type +compiler.misc.binding.pattern={0} _ + +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern={0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern={0}.{1} + compiler.err.initializer.must.be.able.to.complete.normally=Initializer muss normal abgeschlossen werden können compiler.err.initializer.not.allowed=Initializer in Schnittstellen nicht zulässig @@ -1213,6 +1230,9 @@ compiler.note.mref.stat=Methodenreferenz wird übersetzt\nAlternative Metafactor # 0: boolean, 1: symbol compiler.note.mref.stat.1=Methodenreferenz wird übersetzt\nAlternative Metafactory = {0}\nBridgemethode = {1} +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string +compiler.note.lambda.deserialization.stat=Lambda-Deserialisierung wird generiert\nfunctionalInterfaceClass: {0}\nfunctionalInterfaceMethodName: {1}\nFunctionalInterfaceMethodSignature:{2}\nimplMethodKind: {3}\nimplClass: {4}\nimplMethodName: {5}\nimplMethodSignature: {6}\ninstantiatedMethodType: {7} + compiler.note.note=Hinweis:\u0020 # 0: file name @@ -1383,7 +1403,7 @@ compiler.warn.incubating.modules=Inkubatormodul(e) verwendet: {0} # 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} ist veraltet # 0: symbol, 1: symbol @@ -1809,9 +1829,11 @@ compiler.warn.annotation.method.not.found.reason=Annotationsmethode "{1}()" kann compiler.err.cant.attach.type.annotations=Typannotationen {0} können nicht an {1}.{2} angehängt werden:\n{3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=Unbekannte Enum-Konstante {1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=Unbekannte Enum-Konstante {1}.{2}\nGrund: {3} # 0: type, 1: type diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties index 89bdc893a43..a9aaa7ba48b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties @@ -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 @@ -710,7 +710,11 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=ラムダ・ボディ # 0: list of type compiler.err.incompatible.thrown.types.in.mref=機能式でスローされたタイプ{0}は不適合です -compiler.misc.incompatible.arg.types.in.lambda=ラムダ式のパラメータ型は不適合です +# 0: list of type or message segment, 1: list of type or message segment +compiler.misc.incompatible.arg.types.in.lambda=ラムダ式のパラメータ型は不適合です\n期待値: {0}\n検出値: {1} + +# 0: symbol +compiler.misc.wrong.number.args.in.lambda=機能インタフェース{0}のラムダ式のパラメータ数が間違っています compiler.misc.incompatible.arg.types.in.mref=メソッド参照のパラメータ型は不適合です @@ -1046,6 +1050,19 @@ compiler.err.not.exhaustive=switch式がすべての可能な入力値をカバ compiler.err.not.exhaustive.statement=switch文がすべての可能な入力値をカバーしていません +compiler.err.not.exhaustive.details=switch式がすべての可能な入力値をカバーしていません\n欠落パターン: + +compiler.err.not.exhaustive.statement.details=switch文がすべての可能な入力値をカバーしていません\n欠落パターン: + +# 0: type +compiler.misc.binding.pattern={0} _ + +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern={0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern={0}.{1} + compiler.err.initializer.must.be.able.to.complete.normally=初期化子は正常に完了できる必要があります compiler.err.initializer.not.allowed=イニシャライザはinterfacesでは許可されません @@ -1213,6 +1230,9 @@ compiler.note.mref.stat=メソッド参照を変換しています\n代替metafa # 0: boolean, 1: symbol compiler.note.mref.stat.1=メソッド参照を変換しています\n代替metafactory = {0}\nブリッジ・メソッド = {1} +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string +compiler.note.lambda.deserialization.stat=ラムダ・デシリアライゼーションを生成しています\nfunctionalInterfaceClass: {0}\nfunctionalInterfaceMethodName: {1}\nfunctionalInterfaceMethodSignature:{2}\nimplMethodKind: {3}\nimplClass: {4}\nimplMethodName: {5}\nimplMethodSignature: {6}\ninstantiatedMethodType: {7} + compiler.note.note=ノート:\u0020 # 0: file name @@ -1383,7 +1403,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={1}の{0}は推奨されません # 0: symbol, 1: symbol @@ -1809,9 +1829,11 @@ compiler.warn.annotation.method.not.found.reason=タイプ''{0}''内に注釈メ compiler.err.cant.attach.type.annotations=タイプ注釈{0}を{1}.{2}に添付できません:\n{3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=不明な列挙型定数です{1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=不明な列挙型定数です{1}.{2}\n理由: {3} # 0: type, 1: type diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties index 900557a29da..befb5f6262d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties @@ -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 @@ -710,7 +710,11 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=lambda 主体不是 # 0: list of type compiler.err.incompatible.thrown.types.in.mref=函数表达式中抛出的类型 {0} 不兼容 -compiler.misc.incompatible.arg.types.in.lambda=lambda 表达式中的参数类型不兼容 +# 0: list of type or message segment, 1: list of type or message segment +compiler.misc.incompatible.arg.types.in.lambda=lambda 表达式中的参数类型不兼容\n需要:{0}\n找到:{1} + +# 0: symbol +compiler.misc.wrong.number.args.in.lambda=函数接口 {0} 的 lambda 表达式中的参数数量错误 compiler.misc.incompatible.arg.types.in.mref=方法引用中的参数类型不兼容 @@ -1042,9 +1046,22 @@ compiler.misc.cant.apply.diamond.1=无法推断{0}的类型参数\n原因: {1} compiler.err.unreachable.stmt=无法访问的语句 -compiler.err.not.exhaustive=switch 表达式不包含所有可能的输入值 +compiler.err.not.exhaustive=switch 表达式未涵盖所有可能的输入值 -compiler.err.not.exhaustive.statement=并非所有可能的输入值都包含在 switch 语句中 +compiler.err.not.exhaustive.statement=switch 语句未涵盖所有可能的输入值 + +compiler.err.not.exhaustive.details=switch 表达式未涵盖所有可能的输入值\n缺少的模式: + +compiler.err.not.exhaustive.statement.details=switch 语句未涵盖所有可能的输入值\n缺少的模式: + +# 0: type +compiler.misc.binding.pattern={0} _ + +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern={0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern={0}.{1} compiler.err.initializer.must.be.able.to.complete.normally=初始化程序必须能够正常完成 @@ -1213,6 +1230,9 @@ compiler.note.mref.stat=转换方法引用\n替代 metafactory = {0}\n # 0: boolean, 1: symbol compiler.note.mref.stat.1=转换方法引用\n替代 metafactory = {0}\nbridge 方法 = {1} +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string +compiler.note.lambda.deserialization.stat=正在生成 lambda 反序列化\nfunctionalInterfaceClass:{0}\nfunctionalInterfaceMethodName:{1}\nfunctionalInterfaceMethodSignature:{2}\nimplMethodKind:{3}\nimplClass:{4}\nimplMethodName:{5}\nimplMethodSignature:{6}\ninstantiatedMethodType:{7} + compiler.note.note=注:\u0020 # 0: file name @@ -1383,7 +1403,7 @@ compiler.warn.incubating.modules=使用 incubating 模块: {0} # 0: symbol, 1: symbol # lint: deprecation -# flags: aggregate, mandatory, default-enabled +# flags: aggregate, mandatory, default-enabled, deprecation-sensitive compiler.warn.has.been.deprecated={1}中的{0}已过时 # 0: symbol, 1: symbol @@ -1809,9 +1829,11 @@ compiler.warn.annotation.method.not.found.reason=无法找到类型 ''{0}'' 的 compiler.err.cant.attach.type.annotations=无法将类型批注 {0} 附加到 {1}.{2}:\n{3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=未知的枚举常量 {1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=未知的枚举常量 {1}.{2}\n原因: {3} # 0: type, 1: type diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index 1628367d23c..d6fb62142cf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -48,7 +48,7 @@ javac.opt.modulepath=\ javac.opt.sourcepath=\ Specify where to find input source files javac.opt.m=\ - Compile only the specified module(s), check timestamps + Compile only the specified module(s) javac.opt.modulesourcepath=\ Specify where to find input source files for multiple modules javac.opt.bootclasspath=\ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties index 3ae7ab1690e..03b1a7d3350 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties @@ -103,7 +103,7 @@ javac.opt.Xlint=推奨lint警告カテゴリを有効にします。このリリ javac.opt.Xlint.all=すべてのlint警告カテゴリを有効にします javac.opt.Xlint.none=すべてのlint警告カテゴリを無効にします #L10N: do not localize: -Xlint -javac.opt.arg.Xlint=(,)* +javac.opt.arg.Xlint=<キー>(,<キー>)* javac.opt.Xlint.custom=有効または無効にするLint警告カテゴリ(カンマ区切り)。\n指定されたカテゴリを無効にするには、キーの前に''-''を指定します。サポートされているキーと\nデフォルトで有効になっているカテゴリを表示するには、\n''--help-lint''を使用します。 javac.opt.Xlint.desc.auxiliaryclass=ソース・ファイルで非表示になっているが他のファイルから使用されている補助クラスについて警告します。 @@ -206,7 +206,7 @@ javac.opt.printProcessorInfo=プロセッサが処理を依頼される注釈に javac.opt.userpathsfirst=ブート・クラスパスの後ではなく、ブート・クラスパスの前にクラスのクラスパスおよびソース・パスを検索する javac.opt.prefer=暗黙的にコンパイルされるクラスについて、ソース・ファイルとクラス・ファイルの両方が見つかった際どちらを読み込むか指定する # L10N: do not localize: ''preview'' -javac.opt.preview=プレビュー言語機能を有効にします。\nまた、''preview'' lintカテゴリも無効にします。\n-sourceまたは--releaseとともに使用されます。 +javac.opt.preview=プレビュー言語機能を有効にします。\nまた、''preview''lintカテゴリも無効にします。\n-sourceまたは--releaseとともに使用されます。 javac.opt.AT=ファイルからの読取りオプションおよびファイル名 javac.opt.diags=診断モードの選択 javac.opt.addExports=がALL-UNNAMEDである場合、その定義モジュールから、追加モジュールまたは\n すべての名前のないモジュールにエクスポート済とみなされるようにパッケージを指定します。 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties index 447d0d26239..1d1d7250c86 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties @@ -206,7 +206,7 @@ javac.opt.printProcessorInfo=输出有关请求处理程序处理哪些批注的 javac.opt.userpathsfirst=在引导类路径之前而不是之后搜索类的类路径和源路径 javac.opt.prefer=指定读取文件, 当同时找到隐式编译类的源文件和类文件时 # L10N: do not localize: ''preview'' -javac.opt.preview=启用预览语言功能。\n还禁用''preview''lint 类别。\n要与 -source 或 --release 一起使用。 +javac.opt.preview=启用预览语言功能。\n还禁用 ''preview'' lint 类别。\n要与 -source 或 --release 一起使用。 javac.opt.AT=从文件读取选项和文件名 javac.opt.diags=选择诊断模式 javac.opt.addExports=指定被视为已从其定义模块导出到其他模块或者导出到所有\n 未命名模块 (如果 为 ALL-UNNAMED) 的程序包。 diff --git a/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt index 9853f8c70fd..bd9b9177e23 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt @@ -27,7 +27,7 @@ # ########################################################## # module name java.base -header exports java/io,java/lang,java/lang/annotation,java/lang/classfile,java/lang/classfile/attribute,java/lang/classfile/constantpool,java/lang/classfile/instruction,java/lang/constant,java/lang/foreign,java/lang/invoke,java/lang/module,java/lang/ref,java/lang/reflect,java/lang/runtime,java/math,java/net,java/net/spi,java/nio,java/nio/channels,java/nio/channels/spi,java/nio/charset,java/nio/charset/spi,java/nio/file,java/nio/file/attribute,java/nio/file/spi,java/security,java/security/cert,java/security/interfaces,java/security/spec,java/text,java/text/spi,java/time,java/time/chrono,java/time/format,java/time/temporal,java/time/zone,java/util,java/util/concurrent,java/util/concurrent/atomic,java/util/concurrent/locks,java/util/function,java/util/jar,java/util/random,java/util/regex,java/util/spi,java/util/stream,java/util/zip,javax/crypto,javax/crypto/interfaces,javax/crypto/spec,javax/net,javax/net/ssl,javax/security/auth,javax/security/auth/callback,javax/security/auth/login,javax/security/auth/spi,javax/security/auth/x500,javax/security/cert,jdk/internal/event[jdk.jfr],jdk/internal/javac[java.compiler\u005C;u002C;jdk.compiler],jdk/internal/vm/vector[jdk.incubator.vector] extraModulePackages jdk/internal/access/foreign,jdk/internal/classfile/impl,jdk/internal/constant,jdk/internal/foreign/abi,jdk/internal/foreign/abi/aarch64/linux,jdk/internal/foreign/abi/aarch64/macos,jdk/internal/foreign/abi/aarch64/windows,jdk/internal/foreign/abi/fallback,jdk/internal/foreign/abi/ppc64/aix,jdk/internal/foreign/abi/ppc64/linux,jdk/internal/foreign/abi/riscv64/linux,jdk/internal/foreign/abi/s390/linux,jdk/internal/foreign/abi/x64/sysv,jdk/internal/foreign/abi/x64/windows,jdk/internal/foreign/layout,jdk/internal/lang,sun/nio,sun/nio/ch,sun/net,jdk/internal/foreign,jdk/internal/foreign,sun/net,sun/nio/ch uses java/lang/System$LoggerFinder,java/net/ContentHandlerFactory,java/net/spi/InetAddressResolverProvider,java/net/spi/URLStreamHandlerProvider,java/nio/channels/spi/AsynchronousChannelProvider,java/nio/channels/spi/SelectorProvider,java/nio/charset/spi/CharsetProvider,java/nio/file/spi/FileSystemProvider,java/nio/file/spi/FileTypeDetector,java/security/Provider,java/text/spi/BreakIteratorProvider,java/text/spi/CollatorProvider,java/text/spi/DateFormatProvider,java/text/spi/DateFormatSymbolsProvider,java/text/spi/DecimalFormatSymbolsProvider,java/text/spi/NumberFormatProvider,java/time/chrono/AbstractChronology,java/time/chrono/Chronology,java/time/format/DateTimeFormatterPatternProvider,java/time/zone/ZoneRulesProvider,java/util/spi/CalendarDataProvider,java/util/spi/CalendarNameProvider,java/util/spi/CurrencyNameProvider,java/util/spi/LocaleNameProvider,java/util/spi/ResourceBundleControlProvider,java/util/spi/ResourceBundleProvider,java/util/spi/TimeZoneNameProvider,java/util/spi/ToolProvider,javax/security/auth/spi/LoginModule,jdk/internal/io/JdkConsoleProvider,jdk/internal/logger/DefaultLoggerFinder,sun/util/locale/provider/LocaleDataMetaInfo,sun/util/resources/LocaleData$LocaleDataResourceBundleProvider,sun/util/spi/CalendarProvider provides interface\u0020;java/nio/file/spi/FileSystemProvider\u0020;impls\u0020;jdk/internal/jrtfs/JrtFileSystemProvider target macos-aarch64 flags 8000 +header exports java/io,java/lang,java/lang/annotation,java/lang/classfile,java/lang/classfile/attribute,java/lang/classfile/constantpool,java/lang/classfile/instruction,java/lang/constant,java/lang/foreign,java/lang/invoke,java/lang/module,java/lang/ref,java/lang/reflect,java/lang/runtime,java/math,java/net,java/net/spi,java/nio,java/nio/channels,java/nio/channels/spi,java/nio/charset,java/nio/charset/spi,java/nio/file,java/nio/file/attribute,java/nio/file/spi,java/security,java/security/cert,java/security/interfaces,java/security/spec,java/text,java/text/spi,java/time,java/time/chrono,java/time/format,java/time/temporal,java/time/zone,java/util,java/util/concurrent,java/util/concurrent/atomic,java/util/concurrent/locks,java/util/function,java/util/jar,java/util/random,java/util/regex,java/util/spi,java/util/stream,java/util/zip,javax/crypto,javax/crypto/interfaces,javax/crypto/spec,javax/net,javax/net/ssl,javax/security/auth,javax/security/auth/callback,javax/security/auth/login,javax/security/auth/spi,javax/security/auth/x500,javax/security/cert,jdk/internal/event[jdk.jfr],jdk/internal/javac[java.compiler\u005C;u002C;jdk.compiler],jdk/internal/vm/vector[jdk.incubator.vector] extraModulePackages jdk/internal/access/foreign,jdk/internal/classfile/impl,jdk/internal/constant,jdk/internal/foreign/abi,jdk/internal/foreign/abi/aarch64/linux,jdk/internal/foreign/abi/aarch64/macos,jdk/internal/foreign/abi/aarch64/windows,jdk/internal/foreign/abi/fallback,jdk/internal/foreign/abi/ppc64/aix,jdk/internal/foreign/abi/ppc64/linux,jdk/internal/foreign/abi/riscv64/linux,jdk/internal/foreign/abi/s390/linux,jdk/internal/foreign/abi/x64/sysv,jdk/internal/foreign/abi/x64/windows,jdk/internal/foreign/layout,jdk/internal/lang,sun/nio,sun/security/internal,sun/nio/ch,sun/net,jdk/internal/foreign,jdk/internal/foreign,sun/net,sun/nio/ch uses java/lang/System$LoggerFinder,java/net/ContentHandlerFactory,java/net/spi/InetAddressResolverProvider,java/net/spi/URLStreamHandlerProvider,java/nio/channels/spi/AsynchronousChannelProvider,java/nio/channels/spi/SelectorProvider,java/nio/charset/spi/CharsetProvider,java/nio/file/spi/FileSystemProvider,java/nio/file/spi/FileTypeDetector,java/security/Provider,java/text/spi/BreakIteratorProvider,java/text/spi/CollatorProvider,java/text/spi/DateFormatProvider,java/text/spi/DateFormatSymbolsProvider,java/text/spi/DecimalFormatSymbolsProvider,java/text/spi/NumberFormatProvider,java/time/chrono/AbstractChronology,java/time/chrono/Chronology,java/time/format/DateTimeFormatterPatternProvider,java/time/zone/ZoneRulesProvider,java/util/spi/CalendarDataProvider,java/util/spi/CalendarNameProvider,java/util/spi/CurrencyNameProvider,java/util/spi/LocaleNameProvider,java/util/spi/ResourceBundleControlProvider,java/util/spi/ResourceBundleProvider,java/util/spi/TimeZoneNameProvider,java/util/spi/ToolProvider,javax/security/auth/spi/LoginModule,jdk/internal/io/JdkConsoleProvider,jdk/internal/logger/DefaultLoggerFinder,sun/util/locale/provider/LocaleDataMetaInfo,sun/util/resources/LocaleData$LocaleDataResourceBundleProvider,sun/util/spi/CalendarProvider provides interface\u0020;java/nio/file/spi/FileSystemProvider\u0020;impls\u0020;jdk/internal/jrtfs/JrtFileSystemProvider target macos-aarch64 flags 8000 class name java/io/ProxyingConsole header extends java/io/Console flags 30 runtimeAnnotations @Ljdk/internal/ValueBased; @@ -124,7 +124,7 @@ class name java/security/AsymmetricKey header extends java/lang/Object implements java/security/Key,java/security/BinaryEncodable flags 601 class name java/security/BinaryEncodable -header extends java/lang/Object sealed true permittedSubclasses java/security/AsymmetricKey,java/security/KeyPair,java/security/spec/PKCS8EncodedKeySpec,java/security/spec/X509EncodedKeySpec,javax/crypto/EncryptedPrivateKeyInfo,java/security/cert/X509Certificate,java/security/cert/X509CRL,java/security/PEM flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;PEM_API;) +header extends java/lang/Object sealed true permittedSubclasses java/security/AsymmetricKey,java/security/KeyPair,java/security/spec/PKCS8EncodedKeySpec,java/security/spec/X509EncodedKeySpec,javax/crypto/EncryptedPrivateKeyInfo,java/security/cert/X509Certificate,java/security/cert/X509CRL,java/security/PEM,sun/security/internal/InternalBinaryEncodable flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;PEM_API;) -class name java/security/DEREncodable @@ -483,3 +483,6 @@ method name convert descriptor (ILjava/lang/Class;IILjava/lang/Class;IILjdk/inte method name compressExpandOp descriptor (ILjava/lang/Class;Ljava/lang/Class;IILjdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$VectorMask;Ljdk/internal/vm/vector/VectorSupport$CompressExpandOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload; flags 9 signature ;M:Ljdk/internal/vm/vector/VectorSupport$VectorMask;E:Ljava/lang/Object;>(ILjava/lang/Class<+TV;>;Ljava/lang/Class<+TM;>;IITV;TM;Ljdk/internal/vm/vector/VectorSupport$CompressExpandOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload; runtimeAnnotations @Ljdk/internal/vm/annotation/IntrinsicCandidate; method name maskReductionCoerced descriptor (ILjava/lang/Class;IILjdk/internal/vm/vector/VectorSupport$VectorMask;Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)J flags 9 signature ;E:Ljava/lang/Object;>(ILjava/lang/Class<+TM;>;IITM;Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)J runtimeAnnotations @Ljdk/internal/vm/annotation/IntrinsicCandidate; +class name sun/security/internal/InternalBinaryEncodable +header extends java/lang/Object implements java/security/BinaryEncodable flags 31 + 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 c500360f39d..e15e73d0fd4 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -298,7 +298,7 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { 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; + ph->pac_enabled = (auxv->a_un.a_val & HWCAP_PACA) == HWCAP_PACA; #endif } auxv++; 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 9cbde7319f0..4ab1302c9d2 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c @@ -472,7 +472,7 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { } #ifdef __aarch64__ - ph->pac_enabled = HWCAP_PACA & getauxval(AT_HWCAP); + ph->pac_enabled = (HWCAP_PACA & getauxval(AT_HWCAP)) == HWCAP_PACA; #endif // initialize ps_prochandle 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 978fb39ad1c..0258fea6808 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 @@ -75,12 +75,6 @@ public abstract class Frame implements Cloneable { /** Size of ConstMethod for computing BCI from BCP (FIXME: hack) */ private static long ConstMethodSize; - private static int pcReturnOffset; - - public static int pcReturnOffset() { - return pcReturnOffset; - } - protected void adjustForDeopt() { if (pc != null) { // Look for a deopt pc and if it is deopted convert to original pc @@ -104,8 +98,6 @@ public abstract class Frame implements Cloneable { // FIXME: not sure whether alignment here is correct or how to // force it (round up to address size?) ConstMethodSize = ConstMethodType.getSize(); - - pcReturnOffset = db.lookupIntConstant("frame::pc_return_offset").intValue(); } protected int bcpToBci(Address bcp, ConstMethod cm) { diff --git a/src/jdk.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 0a0e77c628f..842a7ec1c9a 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -83,7 +83,7 @@ import com.sun.net.httpserver.*; * If the value is less than or equal to zero, there is no limit. * *

  • {@systemProperty sun.net.httpserver.maxReqTime} (default: -1)
    - * The maximum time in milliseconds allowed to receive a request headers and body. + * The maximum time in seconds allowed to receive a request headers and body. * In practice, the actual time is a function of request size, network speed, and handler * processing delays. A value less than or equal to zero means the time is not limited. * If the limit is exceeded then the connection is terminated and the handler will receive a @@ -91,7 +91,7 @@ import com.sun.net.httpserver.*; * that may mean requests are aborted later than the specified interval. *

  • *
  • {@systemProperty sun.net.httpserver.maxRspTime} (default: -1)
    - * The maximum time in milliseconds allowed to receive a response headers and body. + * The maximum time in seconds allowed to receive a response headers and body. * In practice, the actual time is a function of response size, network speed, and handler * processing delays. A value less than or equal to zero means the time is not limited. * If the limit is exceeded then the connection is terminated and the handler will receive a diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java index 94fe78b9c64..3d77a61c0be 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -538,6 +538,8 @@ class ServerImpl { if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) { // we've hit max limit of current open connections, so we go // ahead and close this connection without processing it + logger.log(Level.DEBUG, "connection limit reached, " + + "closing accepted connection " + chan); try { chan.close(); } catch (IOException ignore) { 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 9ac90c08c27..9fac32da26d 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 @@ -36,7 +36,9 @@ abstract sealed class AbstractMask extends VectorMask 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 { + ShortVector64.ShortMask64, ShortVector128.ShortMask128, ShortVector256.ShortMask256, ShortVector512.ShortMask512, ShortVectorMax.ShortMaskMax, + Float16Vector64.Float16Mask64, Float16Vector128.Float16Mask128, Float16Vector256.Float16Mask256, Float16Vector512.Float16Mask512, + Float16VectorMax.Float16MaskMax { 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 bea495f74fc..8c22329a833 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 @@ -35,7 +35,8 @@ abstract sealed class AbstractShuffle extends VectorShuffle 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 { + ShortVector64.ShortShuffle64, ShortVector128.ShortShuffle128, ShortVector256.ShortShuffle256, ShortVector512.ShortShuffle512, ShortVectorMax.ShortShuffleMax, + Float16Vector64.Float16Shuffle64, Float16Vector128.Float16Shuffle128, Float16Vector256.Float16Shuffle256, Float16Vector512.Float16Shuffle512, Float16VectorMax.Float16ShuffleMax { 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 3fd2be34346..fa338f27975 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 @@ -37,7 +37,7 @@ import jdk.internal.vm.annotation.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 { + IntVector.IntSpecies, LongVector.LongSpecies, ShortVector.ShortSpecies, Float16Vector.Float16Species { final VectorShape vectorShape; final LaneType laneType; final int laneCount; @@ -424,14 +424,21 @@ abstract sealed class AbstractSpecies extends jdk.internal.vm.vector.VectorSu Object ia = Array.newInstance(carrierType(), laneCount); assert(ia.getClass() == laneType.arrayType); checkValue(laneCount-1); // worst case - for (int i = 0; i < laneCount; i++) { - if ((byte)i == i) - Array.setByte(ia, i, (byte)i); - else if ((short)i == i) - Array.setShort(ia, i, (short)i); - else - Array.setInt(ia, i, i); - assert(Array.getDouble(ia, i) == i); + if (elementType() == Float16.class) { + for (int i = 0; i < laneCount; i++) { + Array.setShort(ia, i, Float.floatToFloat16((float)i)); + assert(Float16.shortBitsToFloat16(Array.getShort(ia, i)).intValue() == i); + } + } else { + for (int i = 0; i < laneCount; i++) { + if ((byte)i == i) + Array.setByte(ia, i, (byte)i); + else if ((short)i == i) + Array.setShort(ia, i, (short)i); + else + Array.setInt(ia, i, i); + assert(Array.getDouble(ia, i) == i); + } } return ia; } @@ -629,6 +636,8 @@ abstract sealed class AbstractSpecies extends jdk.internal.vm.vector.VectorSu s = IntVector.species(shape); break; case LaneType.SK_LONG: s = LongVector.species(shape); break; + case LaneType.SK_FLOAT16: + s = Float16Vector.species(shape); break; } if (s == null) { // NOTE: The result of this method is guaranteed to be 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 4efd0d24af9..cf6602573a8 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 @@ -35,7 +35,7 @@ import static jdk.incubator.vector.VectorOperators.*; @SuppressWarnings("cast") abstract sealed class AbstractVector extends Vector - permits ByteVector, DoubleVector, FloatVector, IntVector, LongVector, ShortVector { + permits ByteVector, DoubleVector, FloatVector, IntVector, LongVector, ShortVector, Float16Vector { /** * The order of vector bytes when stored in natural, * array elements of the same lane type. @@ -331,6 +331,15 @@ abstract sealed class AbstractVector extends Vector return (DoubleVector) asVectorRaw(LaneType.DOUBLE); } + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public Float16Vector reinterpretAsFloat16s() { + return (Float16Vector) asVectorRaw(LaneType.FLOAT16); + } + /** * {@inheritDoc} */ @@ -682,6 +691,8 @@ abstract sealed class AbstractVector extends Vector return FloatVector.fromMemorySegment(rsp.check(float.class), ms, 0, bo, m.check(float.class)).check0(rsp); case LaneType.SK_DOUBLE: return DoubleVector.fromMemorySegment(rsp.check(double.class), ms, 0, bo, m.check(double.class)).check0(rsp); + case LaneType.SK_FLOAT16: + return Float16Vector.fromMemorySegment(rsp.check(Float16.class), ms, 0, bo, m.check(Float16.class)).check0(rsp); default: throw new AssertionError(rsp.toString()); } @@ -744,6 +755,13 @@ abstract sealed class AbstractVector extends Vector } return DoubleVector.fromArray(dsp.check(double.class), a, 0).check0(dsp); } + case LaneType.SK_FLOAT16: { + short[] a = new short[rlength]; + for (int i = 0; i < limit; i++) { + a[i] = Float16.float16ToRawShortBits(Float16.valueOf((float) lanes[i])); + } + return Float16Vector.fromArray(dsp.check(Float16.class), a, 0).check0(dsp); + } default: break; } } else { @@ -794,6 +812,13 @@ abstract sealed class AbstractVector extends Vector } return DoubleVector.fromArray(dsp.check(double.class), a, 0).check0(dsp); } + case LaneType.SK_FLOAT16: { + short[] a = new short[rlength]; + for (int i = 0; i < limit; i++) { + a[i] = Float16.float16ToRawShortBits(Float16.valueOf((float) lanes[i])); + } + return Float16Vector.fromArray(dsp.check(Float16.class), a, 0).check0(dsp); + } default: break; } } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java new file mode 100644 index 00000000000..a42fb44dd02 --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java @@ -0,0 +1,4101 @@ +/* + * 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 + * 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 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.Function; + +import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.vector.VectorSupport; + +import static jdk.internal.vm.vector.VectorSupport.*; +import static jdk.incubator.vector.VectorIntrinsics.*; + +import static jdk.incubator.vector.VectorOperators.*; +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import static java.lang.Float.*; + +// -- This file was mechanically generated: Do not edit! -- // + +/** + * A specialized {@link Vector} representing an ordered immutable sequence of + * 16-bit data values in the IEEE 754 binary16 format. + *

    + * The scalar {@linkplain Float16Vector#elementType() element type} of {@code Float16Vector} + * is the class {@link Float16}, a value-based + * class holding 16-bit data in IEEE 754 binary16 format. However, the {@code Float16} + * class is not used by vector operations that accept scalar element values, or + * arrays of scalar element values. Instead, the primitive type {@code short} is + * used to explicitly hold 16-bit data in IEEE 754 binary16 format. For such operations + * it may be necessary to explicitly convert between floating-point values of {@code Float16} + * or {@code float} and values of {@code short} using the appropriate conversion + * methods on {@code Float16} or {@code Float}. + * + *

    + * The specifications for operations on elements of this class are written as if + * {@code Float16} is a primitive floating-point type. An operation referencing a + * Java operator is mapped to a method on {@code Float16} that specifies that + * operator's semantics. For example, the semantics of the {@code +} operator, + * as referenced by {@link Vector#add(Vector)} and {@link VectorOperators#ADD}, + * is mapped to the method {@link Float16#add(Float16, Float16)}. + * An operation referencing a method on {@link Math} is mapped to a method of the + * same name on {@code Float16}, if it exists. For example, {@link Math#fma} is + * mapped to {@link Float16#fma}, as referenced by {@link Float16Vector#fma(short, short)} + * and {@link VectorOperators#FMA}. + * Otherwise, if there is no equivalent method on {@code Float16}, the expression that is + * an invocation of a method on {@code Math} is mapped to an expression that converts + * the {@code Float16} arguments to {@code double} values or {@code float} values as + * required by the method's parameter types, invokes the method on {@code Math} with + * the converted values, and converts the resulting {@code double} or {@code float} value + * to a {@code Float16} value. For example, {@link Math#sin} is mapped to the expression + * {@code Float16.valueOf(Math.sin(a.doubleValue()))}, where {@code a} is the + * {@code Float16} lane value, as referenced by {@link VectorOperators#SIN}. + * + * @apiNote + * {@code Float16} is currently a value-based class and therefore cannot be optimally + * used as the scalar element type of vector operations until it becomes a value class + * that behaves similarly to the primitive type {@code short} and to arrays of {@code short}. + * For example, accessing {@code Float16} vectors using arrays requires those arrays be + * {@code short[]} arrays. Accessing vectors using memory segments requires, naturally, + * that consecutive 16-bits of memory hold 16-bit data values in the IEEE 754 binary16 + * format. + * @see Float16 + * @see Float16#float16ToRawShortBits(Float16) + * @see Float16#shortBitsToFloat16(short) + * @see Float#floatToFloat16(float) + * @see Float#float16ToFloat(short) + */ +@SuppressWarnings("cast") // warning: redundant cast +public abstract sealed class Float16Vector extends AbstractVector + permits Float16Vector64, Float16Vector128, Float16Vector256, Float16Vector512, Float16VectorMax { + + Float16Vector(short[] vec) { + super(vec); + } + + static final int FORBID_OPCODE_KIND = VO_NOFP; + + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + + static final int LANE_TYPE_ORDINAL = LT_FLOAT16; + + static final int LANEBITS_TYPE_ORDINAL = LT_SHORT; + + @ForceInline + static int opCode(Operator op) { + return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); + } + @ForceInline + static int opCode(Operator op, int requireKind) { + requireKind |= VO_OPCODE_VALID; + return VectorOperators.opCode(op, requireKind, FORBID_OPCODE_KIND); + } + @ForceInline + static boolean opKind(Operator op, int bit) { + return VectorOperators.opKind(op, bit); + } + + // Virtualized factories and operators, + // coded with portable definitions. + // These are all @ForceInline in case + // they need to be used performantly. + // The various shape-specific subclasses + // also specialize them by wrapping + // them in a call like this: + // return (ByteVector128) + // super.bOp((ByteVector128) o); + // The purpose of that is to forcibly inline + // the generic definition from this file + // into a sharply-typed and size-specific + // wrapper in the subclass file, so that + // the JIT can specialize the code. + // The code is only inlined and expanded + // if it gets hot. Think of it as a cheap + // and lazy version of C++ templates. + + // Virtualized getter + + /*package-private*/ + abstract short[] vec(); + + // Virtualized constructors + + /** + * Build a vector directly using my own constructor. + * It is an error if the array is aliased elsewhere. + */ + /*package-private*/ + abstract Float16Vector vectorFactory(short[] vec); + + /** + * Build a mask directly using my species. + * It is an error if the array is aliased elsewhere. + */ + /*package-private*/ + @ForceInline + final + AbstractMask maskFactory(boolean[] bits) { + return vspecies().maskFactory(bits); + } + + // Constant loader (takes dummy as vector arg) + interface FVOp { + short apply(int i); + } + + /*package-private*/ + @ForceInline + final + Float16Vector vOp(FVOp f) { + short[] res = new short[length()]; + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i); + } + return vectorFactory(res); + } + + @ForceInline + final + Float16Vector vOp(VectorMask m, FVOp f) { + short[] res = new short[length()]; + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + if (mbits[i]) { + res[i] = f.apply(i); + } + } + return vectorFactory(res); + } + + // Unary operator + + /*package-private*/ + interface FUnOp { + float apply(int i, float a); + } + + /*package-private*/ + abstract + Float16Vector uOp(FUnOp f); + @ForceInline + final + Float16Vector uOpTemplate(FUnOp f) { + short[] vec = vec(); + short[] res = new short[length()]; + for (int i = 0; i < res.length; i++) { + res[i] = floatToFloat16(f.apply(i, float16ToFloat(vec[i]))); + } + return vectorFactory(res); + } + + /*package-private*/ + abstract + Float16Vector uOp(VectorMask m, + FUnOp f); + @ForceInline + final + Float16Vector uOpTemplate(VectorMask m, + FUnOp f) { + if (m == null) { + return uOpTemplate(f); + } + short[] vec = vec(); + short[] res = new short[length()]; + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = mbits[i] ? floatToFloat16(f.apply(i, float16ToFloat(vec[i]))) : vec[i]; + } + return vectorFactory(res); + } + + /*package-private*/ + interface FUnRawOp { + short apply(int i, short a); + } + + /*package-private*/ + abstract + Float16Vector uRawOp(FUnRawOp f); + @ForceInline + final + Float16Vector uRawOpTemplate(FUnRawOp f) { + short[] vec = vec(); + short[] res = new short[length()]; + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, vec[i]); + } + return vectorFactory(res); + } + + /*package-private*/ + abstract + Float16Vector uRawOp(VectorMask m, + FUnRawOp f); + @ForceInline + final + Float16Vector uRawOpTemplate(VectorMask m, + FUnRawOp f) { + if (m == null) { + return uRawOpTemplate(f); + } + short[] vec = vec(); + short[] res = new short[length()]; + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = mbits[i] ? f.apply(i, vec[i]) : vec[i]; + } + return vectorFactory(res); + } + // Binary operator + + /*package-private*/ + interface FBinOp { + float apply(int i, float a, float b); + } + + /*package-private*/ + abstract + Float16Vector bOp(Vector o, + FBinOp f); + @ForceInline + final + Float16Vector bOpTemplate(Vector o, + FBinOp f) { + short[] res = new short[length()]; + short[] vec1 = this.vec(); + short[] vec2 = ((Float16Vector)o).vec(); + for (int i = 0; i < res.length; i++) { + res[i] = floatToFloat16(f.apply(i, float16ToFloat(vec1[i]), float16ToFloat(vec2[i]))); + } + return vectorFactory(res); + } + + /*package-private*/ + abstract + Float16Vector bOp(Vector o, + VectorMask m, + FBinOp f); + @ForceInline + final + Float16Vector bOpTemplate(Vector o, + VectorMask m, + FBinOp f) { + if (m == null) { + return bOpTemplate(o, f); + } + short[] res = new short[length()]; + short[] vec1 = this.vec(); + short[] vec2 = ((Float16Vector)o).vec(); + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = mbits[i] ? floatToFloat16(f.apply(i, float16ToFloat(vec1[i]), float16ToFloat(vec2[i]))) : vec1[i]; + } + return vectorFactory(res); + } + + // Ternary operator + + /*package-private*/ + interface FTriOp { + short apply(int i, short a, short b, short c); + } + + /*package-private*/ + abstract + Float16Vector tOp(Vector o1, + Vector o2, + FTriOp f); + @ForceInline + final + Float16Vector tOpTemplate(Vector o1, + Vector o2, + FTriOp f) { + short[] res = new short[length()]; + short[] vec1 = this.vec(); + short[] vec2 = ((Float16Vector)o1).vec(); + short[] vec3 = ((Float16Vector)o2).vec(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, vec1[i], vec2[i], vec3[i]); + } + return vectorFactory(res); + } + + /*package-private*/ + abstract + Float16Vector tOp(Vector o1, + Vector o2, + VectorMask m, + FTriOp f); + @ForceInline + final + Float16Vector tOpTemplate(Vector o1, + Vector o2, + VectorMask m, + FTriOp f) { + if (m == null) { + return tOpTemplate(o1, o2, f); + } + short[] res = new short[length()]; + short[] vec1 = this.vec(); + short[] vec2 = ((Float16Vector)o1).vec(); + short[] vec3 = ((Float16Vector)o2).vec(); + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = mbits[i] ? f.apply(i, vec1[i], vec2[i], vec3[i]) : vec1[i]; + } + return vectorFactory(res); + } + + // Reduction operator + + /*package-private*/ + abstract + short rOp(short v, VectorMask m, FBinOp f); + + @ForceInline + final + short rOpTemplate(short v, VectorMask m, FBinOp f) { + if (m == null) { + return rOpTemplate(v, f); + } + short[] vec = vec(); + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < vec.length; i++) { + v = mbits[i] ? floatToFloat16(f.apply(i, float16ToFloat(v), float16ToFloat(vec[i]))) : v; + } + return v; + } + + @ForceInline + final + short rOpTemplate(short v, FBinOp f) { + short[] vec = vec(); + for (int i = 0; i < vec.length; i++) { + v = floatToFloat16(f.apply(i, float16ToFloat(v), float16ToFloat(vec[i]))); + } + return v; + } + + // Memory reference + + /*package-private*/ + interface FLdOp { + short apply(M memory, int offset, int i); + } + + /*package-private*/ + @ForceInline + final + Float16Vector ldOp(M memory, int offset, + FLdOp f) { + //dummy; no vec = vec(); + short[] res = new short[length()]; + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(memory, offset, i); + } + return vectorFactory(res); + } + + /*package-private*/ + @ForceInline + final + Float16Vector ldOp(M memory, int offset, + VectorMask m, + FLdOp f) { + //short[] vec = vec(); + short[] res = new short[length()]; + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + if (mbits[i]) { + res[i] = f.apply(memory, offset, i); + } + } + return vectorFactory(res); + } + + /*package-private*/ + interface FLdLongOp { + short apply(MemorySegment memory, long offset, int i); + } + + /*package-private*/ + @ForceInline + final + Float16Vector ldLongOp(MemorySegment memory, long offset, + FLdLongOp f) { + //dummy; no vec = vec(); + short[] res = new short[length()]; + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(memory, offset, i); + } + return vectorFactory(res); + } + + /*package-private*/ + @ForceInline + final + Float16Vector ldLongOp(MemorySegment memory, long offset, + VectorMask m, + FLdLongOp f) { + //short[] vec = vec(); + short[] res = new short[length()]; + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + if (mbits[i]) { + res[i] = f.apply(memory, offset, i); + } + } + return vectorFactory(res); + } + + static short memorySegmentGet(MemorySegment ms, long o, int i) { + return ms.get(ELEMENT_LAYOUT, o + i * 2L); + } + + interface FStOp { + void apply(M memory, int offset, int i, short a); + } + + /*package-private*/ + @ForceInline + final + void stOp(M memory, int offset, + FStOp f) { + short[] vec = vec(); + for (int i = 0; i < vec.length; i++) { + f.apply(memory, offset, i, vec[i]); + } + } + + /*package-private*/ + @ForceInline + final + void stOp(M memory, int offset, + VectorMask m, + FStOp f) { + short[] vec = vec(); + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < vec.length; i++) { + if (mbits[i]) { + f.apply(memory, offset, i, vec[i]); + } + } + } + + interface FStLongOp { + void apply(MemorySegment memory, long offset, int i, short a); + } + + /*package-private*/ + @ForceInline + final + void stLongOp(MemorySegment memory, long offset, + FStLongOp f) { + short[] vec = vec(); + for (int i = 0; i < vec.length; i++) { + f.apply(memory, offset, i, vec[i]); + } + } + + /*package-private*/ + @ForceInline + final + void stLongOp(MemorySegment memory, long offset, + VectorMask m, + FStLongOp f) { + short[] vec = vec(); + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < vec.length; i++) { + if (mbits[i]) { + f.apply(memory, offset, i, vec[i]); + } + } + } + + static void memorySegmentSet(MemorySegment ms, long o, int i, short e) { + ms.set(ELEMENT_LAYOUT, o + i * 2L, e); + } + + // Binary test + + /*package-private*/ + interface FBinTest { + boolean apply(int cond, int i, short a, short b); + } + + /*package-private*/ + @ForceInline + final + AbstractMask bTest(int cond, + Vector o, + FBinTest f) { + short[] vec1 = vec(); + short[] vec2 = ((Float16Vector)o).vec(); + boolean[] bits = new boolean[length()]; + for (int i = 0; i < length(); i++){ + bits[i] = f.apply(cond, i, vec1[i], vec2[i]); + } + return maskFactory(bits); + } + + + /*package-private*/ + @Override + abstract Float16Species vspecies(); + + /*package-private*/ + @ForceInline + static long toBits(short e) { + return e; + } + + /*package-private*/ + @ForceInline + static short fromBits(long bits) { + return (short)bits; + } + + static Float16Vector expandHelper(Vector v, VectorMask m) { + VectorSpecies vsp = m.vectorSpecies(); + Float16Vector r = (Float16Vector) vsp.zero(); + Float16Vector vi = (Float16Vector) v; + if (m.allTrue()) { + return vi; + } + for (int i = 0, j = 0; i < vsp.length(); i++) { + if (m.laneIsSet(i)) { + r = r.withLane(i, vi.lane(j++)); + } + } + return r; + } + + static Float16Vector compressHelper(Vector v, VectorMask m) { + VectorSpecies vsp = m.vectorSpecies(); + Float16Vector r = (Float16Vector) vsp.zero(); + Float16Vector vi = (Float16Vector) v; + if (m.allTrue()) { + return vi; + } + for (int i = 0, j = 0; i < vsp.length(); i++) { + if (m.laneIsSet(i)) { + r = r.withLane(j++, vi.lane(i)); + } + } + return r; + } + + static Float16Vector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + short[] res = new short[vlen]; + short[] vecPayload1 = ((Float16Vector)indexes).vec(); + short[] vecPayload2 = ((Float16Vector)src1).vec(); + short[] vecPayload3 = ((Float16Vector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int index = shortBitsToFloat16(vecPayload1[i]).intValue(); + int wrapped_index = VectorIntrinsics.wrapToRange(index, 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((Float16Vector)src1).vectorFactory(res); + } + + // Static factories (other than memory operations) + + // Note: A surprising behavior in javadoc + // sometimes makes a lone /** {@inheritDoc} */ + // comment drop the method altogether, + // apparently if the method mentions a + // parameter or return type of Vector + // instead of Vector as originally specified. + // Adding an empty HTML fragment appears to + // nudge javadoc into providing the desired + // inherited documentation. We use the HTML + // comment for this. + + /** + * Returns a vector of the given species + * where all lane elements are set to + * zero, the default primitive value. + * + * @param species species of the desired zero vector + * @return a zero vector + */ + @ForceInline + public static Float16Vector zero(VectorSpecies species) { + Float16Species vsp = (Float16Species) species; + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), + toBits((short) 0), MODE_BROADCAST, vsp, + ((bits_, s_) -> s_.rvOp(i -> bits_))); + } + + /** + * Returns a vector of the same species as this one + * where all lane elements are set to + * the primitive value {@code e}. + * + * The contents of the current vector are discarded; + * only the species is relevant to this operation. + * + *

    This method returns the value of this expression: + * {@code Float16Vector.broadcast(this.species(), e)}. + * + * @apiNote + * Unlike the similar method named {@code broadcast()} + * in the supertype {@code Vector}, this method does not + * need to validate its argument, and cannot throw + * {@code IllegalArgumentException}. This method is + * therefore preferable to the supertype method. + * + * @param e the value to broadcast + * @return a vector where all lane elements are set to + * the primitive value {@code e} + * @see #broadcast(VectorSpecies,long) + * @see Vector#broadcast(long) + * @see VectorSpecies#broadcast(long) + */ + public abstract Float16Vector broadcast(short e); + + /** + * Returns a vector of the given species + * where all lane elements are set to + * the primitive value {@code e}. + * + * @param species species of the desired vector + * @param e the value to broadcast + * @return a vector where all lane elements are set to + * the primitive value {@code e} + * @see #broadcast(long) + * @see Vector#broadcast(long) + * @see VectorSpecies#broadcast(long) + */ + @ForceInline + public static Float16Vector broadcast(VectorSpecies species, short e) { + Float16Species vsp = (Float16Species) species; + return vsp.broadcast(e); + } + + /*package-private*/ + @ForceInline + final Float16Vector broadcastTemplate(short e) { + Float16Species vsp = vspecies(); + return vsp.broadcast(e); + } + + /** + * {@inheritDoc} + * @apiNote + * When working with vector subtypes like {@code Float16Vector}, + * {@linkplain #broadcast(short) the more strongly typed method} + * is typically selected. It can be explicitly selected + * using a cast: {@code v.broadcast((short)e)}. + * The two expressions will produce numerically identical results. + */ + @Override + public abstract Float16Vector broadcast(long e); + + /** + * Returns a vector of the given species + * where all lane elements are set to + * the primitive value {@code e}. + * + * The {@code long} value must be accurately representable + * by the {@code ETYPE} of the vector species, so that + * {@code e==(long)(ETYPE)e}. + * + * @param species species of the desired vector + * @param e the value to broadcast + * @return a vector where all lane elements are set to + * the primitive value {@code e} + * @throws IllegalArgumentException + * if the given {@code long} value cannot + * be represented by the vector's {@code ETYPE} + * @see #broadcast(VectorSpecies,short) + * @see VectorSpecies#checkValue(long) + */ + @ForceInline + public static Float16Vector broadcast(VectorSpecies species, long e) { + Float16Species vsp = (Float16Species) species; + return vsp.broadcast(e); + } + + /*package-private*/ + @ForceInline + final Float16Vector broadcastTemplate(long e) { + return vspecies().broadcast(e); + } + + // Unary lanewise support + + /** + * {@inheritDoc} + */ + public abstract + Float16Vector lanewise(VectorOperators.Unary op); + + @ForceInline + final + Float16Vector lanewiseTemplate(VectorOperators.Unary op) { + if (opKind(op, VO_SPECIAL)) { + if (op == ZOMO) { + return blend(broadcast(-1), compare(NE, 0)); + } + else if (opKind(op, VO_MATHLIB)) { + return unaryMathOp(op); + } + } + int opc = opCode(op); + return VectorSupport.unaryOp( + opc, getClass(), null, laneTypeOrdinal(), length(), + this, null, + UN_IMPL.find(op, opc, Float16Vector::unaryOperations)); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector lanewise(VectorOperators.Unary op, + VectorMask m); + @ForceInline + final + Float16Vector lanewiseTemplate(VectorOperators.Unary op, + Class> maskClass, + VectorMask m) { + m.check(maskClass, this); + if (opKind(op, VO_SPECIAL)) { + if (op == ZOMO) { + return blend(broadcast(-1), compare(NE, 0, m)); + } + else if (opKind(op, VO_MATHLIB)) { + return blend(unaryMathOp(op), m); + } + } + int opc = opCode(op); + return VectorSupport.unaryOp( + opc, getClass(), maskClass, laneTypeOrdinal(), length(), + this, m, + UN_IMPL.find(op, opc, Float16Vector::unaryOperations)); + } + + @ForceInline + final + Float16Vector unaryMathOp(VectorOperators.Unary op) { + return VectorMathLibrary.unaryMathOp(op, opCode(op), vspecies(), Float16Vector::unaryOperations, + this); + } + + private static final + ImplCache>> + UN_IMPL = new ImplCache<>(Unary.class, Float16Vector.class); + + private static UnaryOperation> unaryOperations(int opc_) { + switch (opc_) { + case VECTOR_OP_NEG: return (v0, m) -> + v0.uOp(m, (i, a) -> Float16.negate(Float16.valueOf(a)).floatValue()); + case VECTOR_OP_ABS: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.abs(a)); + case VECTOR_OP_SIN: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.sin(a)); + case VECTOR_OP_COS: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.cos(a)); + case VECTOR_OP_TAN: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.tan(a)); + case VECTOR_OP_ASIN: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.asin(a)); + case VECTOR_OP_ACOS: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.acos(a)); + case VECTOR_OP_ATAN: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.atan(a)); + case VECTOR_OP_EXP: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.exp(a)); + case VECTOR_OP_LOG: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.log(a)); + case VECTOR_OP_LOG10: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.log10(a)); + case VECTOR_OP_SQRT: return (v0, m) -> + v0.uOp(m, (i, a) -> Float16.sqrt(Float16.valueOf(a)).floatValue()); + case VECTOR_OP_CBRT: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.cbrt(a)); + case VECTOR_OP_SINH: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.sinh(a)); + case VECTOR_OP_COSH: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.cosh(a)); + case VECTOR_OP_TANH: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.tanh(a)); + case VECTOR_OP_EXPM1: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.expm1(a)); + case VECTOR_OP_LOG1P: return (v0, m) -> + v0.uOp(m, (i, a) -> (float) Math.log1p(a)); + default: return null; + } + } + + // Binary lanewise support + + /** + * {@inheritDoc} + * @see #lanewise(VectorOperators.Binary,short) + * @see #lanewise(VectorOperators.Binary,short,VectorMask) + */ + @Override + public abstract + Float16Vector lanewise(VectorOperators.Binary op, + Vector v); + @ForceInline + final + Float16Vector lanewiseTemplate(VectorOperators.Binary op, + Vector v) { + Float16Vector that = (Float16Vector) v; + that.check(this); + + if (opKind(op, VO_SPECIAL )) { + if (op == FIRST_NONZERO) { + VectorMask mask + = this.viewAsIntegralLanes().compare(EQ, (short) 0); + return this.blend(that, mask.cast(vspecies())); + } + else if (opKind(op, VO_MATHLIB)) { + return binaryMathOp(op, that); + } + } + + int opc = opCode(op); + return VectorSupport.binaryOp( + opc, getClass(), null, laneTypeOrdinal(), length(), + this, that, null, + BIN_IMPL.find(op, opc, Float16Vector::binaryOperations)); + } + + /** + * {@inheritDoc} + * @see #lanewise(VectorOperators.Binary,short,VectorMask) + */ + @Override + public abstract + Float16Vector lanewise(VectorOperators.Binary op, + Vector v, + VectorMask m); + @ForceInline + final + Float16Vector lanewiseTemplate(VectorOperators.Binary op, + Class> maskClass, + Vector v, VectorMask m) { + Float16Vector that = (Float16Vector) v; + that.check(this); + m.check(maskClass, this); + + if (opKind(op, VO_SPECIAL )) { + if (op == FIRST_NONZERO) { + ShortVector bits = this.viewAsIntegralLanes(); + VectorMask mask + = bits.compare(EQ, (short) 0, m.cast(bits.vspecies())); + return this.blend(that, mask.cast(vspecies())); + } + else if (opKind(op, VO_MATHLIB)) { + return this.blend(binaryMathOp(op, that), m); + } + + } + + int opc = opCode(op); + return VectorSupport.binaryOp( + opc, getClass(), maskClass, laneTypeOrdinal(), length(), + this, that, m, + BIN_IMPL.find(op, opc, Float16Vector::binaryOperations)); + } + + @ForceInline + final + Float16Vector binaryMathOp(VectorOperators.Binary op, Float16Vector that) { + return VectorMathLibrary.binaryMathOp(op, opCode(op), vspecies(), Float16Vector::binaryOperations, + this, that); + } + + private static final + ImplCache>> + BIN_IMPL = new ImplCache<>(Binary.class, Float16Vector.class); + + private static BinaryOperation> binaryOperations(int opc_) { + switch (opc_) { + case VECTOR_OP_ADD: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float)(a + b)); + case VECTOR_OP_SUB: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float)(a - b)); + case VECTOR_OP_MUL: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float)(a * b)); + case VECTOR_OP_DIV: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float)(a / b)); + case VECTOR_OP_MAX: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float)Math.max(a, b)); + case VECTOR_OP_MIN: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float)Math.min(a, b)); + case VECTOR_OP_OR: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> FloatVector.fromBits(FloatVector.toBits(a) | FloatVector.toBits(b))); + case VECTOR_OP_ATAN2: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float) Math.atan2(a, b)); + case VECTOR_OP_POW: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float) Math.pow(a, b)); + case VECTOR_OP_HYPOT: return (v0, v1, vm) -> + v0.bOp(v1, vm, (i, a, b) -> (float) Math.hypot(a, b)); + default: return null; + } + } + + // FIXME: Maybe all of the public final methods in this file (the + // simple ones that just call lanewise) should be pushed down to + // the X-VectorBits template. They can't optimize properly at + // this level, and must rely on inlining. Does it work? + // (If it works, of course keep the code here.) + + /** + * Combines the lane values of this vector + * with the value of a broadcast scalar. + * + * This is a lane-wise binary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, this.broadcast(e))}. + * + * @param op the operation used to process lane values + * @param e the input scalar + * @return the result of applying the operation lane-wise + * to the two input vectors + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short,VectorMask) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Binary op, + short e) { + return lanewise(op, broadcast(e)); + } + + /** + * Combines the lane values of this vector + * with the value of a broadcast scalar, + * with selection of lane elements controlled by a mask. + * + * This is a masked lane-wise binary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, this.broadcast(e), m)}. + * + * @param op the operation used to process lane values + * @param e the input scalar + * @param m the mask controlling lane selection + * @return the result of applying the operation lane-wise + * to the input vector and the scalar + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Binary,Vector,VectorMask) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Binary op, + short e, + VectorMask m) { + return lanewise(op, broadcast(e), m); + } + + /** + * {@inheritDoc} + * @apiNote + * When working with vector subtypes like {@code Float16Vector}, + * {@linkplain #lanewise(VectorOperators.Binary,short) + * the more strongly typed method} + * is typically selected. It can be explicitly selected + * using a cast: {@code v.lanewise(op,(short)e)}. + * The two expressions will produce numerically identical results. + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Binary op, + long e) { + short e1 = float16ToRawShortBits(Float16.valueOf(e)); + if (shortBitsToFloat16(e1).longValue() != e) { + vspecies().checkValue(e); // for exception + } + return lanewise(op, e1); + } + + /** + * {@inheritDoc} + * @apiNote + * When working with vector subtypes like {@code Float16Vector}, + * {@linkplain #lanewise(VectorOperators.Binary,short,VectorMask) + * the more strongly typed method} + * is typically selected. It can be explicitly selected + * using a cast: {@code v.lanewise(op,(short)e,m)}. + * The two expressions will produce numerically identical results. + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Binary op, + long e, VectorMask m) { + short e1 = float16ToRawShortBits(Float16.valueOf(e)); + if (shortBitsToFloat16(e1).longValue() != e) { + vspecies().checkValue(e); // for exception + } + return lanewise(op, e1, m); + } + + + // Ternary lanewise support + + // Ternary operators come in eight variations: + // lanewise(op, [broadcast(e1)|v1], [broadcast(e2)|v2]) + // lanewise(op, [broadcast(e1)|v1], [broadcast(e2)|v2], mask) + + // It is annoying to support all of these variations of masking + // and broadcast, but it would be more surprising not to continue + // the obvious pattern started by unary and binary. + + /** + * {@inheritDoc} + * @see #lanewise(VectorOperators.Ternary,short,short,VectorMask) + * @see #lanewise(VectorOperators.Ternary,Vector,short,VectorMask) + * @see #lanewise(VectorOperators.Ternary,short,Vector,VectorMask) + * @see #lanewise(VectorOperators.Ternary,short,short) + * @see #lanewise(VectorOperators.Ternary,Vector,short) + * @see #lanewise(VectorOperators.Ternary,short,Vector) + */ + @Override + public abstract + Float16Vector lanewise(VectorOperators.Ternary op, + Vector v1, + Vector v2); + @ForceInline + final + Float16Vector lanewiseTemplate(VectorOperators.Ternary op, + Vector v1, + Vector v2) { + Float16Vector that = (Float16Vector) v1; + Float16Vector tother = (Float16Vector) v2; + // It's a word: https://www.dictionary.com/browse/tother + // See also Chapter 11 of Dickens, Our Mutual Friend: + // "Totherest Governor," replied Mr Riderhood... + that.check(this); + tother.check(this); + int opc = opCode(op); + return VectorSupport.ternaryOp( + opc, getClass(), null, laneTypeOrdinal(), length(), + this, that, tother, null, + TERN_IMPL.find(op, opc, Float16Vector::ternaryOperations)); + } + + /** + * {@inheritDoc} + * @see #lanewise(VectorOperators.Ternary,short,short,VectorMask) + * @see #lanewise(VectorOperators.Ternary,Vector,short,VectorMask) + * @see #lanewise(VectorOperators.Ternary,short,Vector,VectorMask) + */ + @Override + public abstract + Float16Vector lanewise(VectorOperators.Ternary op, + Vector v1, + Vector v2, + VectorMask m); + @ForceInline + final + Float16Vector lanewiseTemplate(VectorOperators.Ternary op, + Class> maskClass, + Vector v1, + Vector v2, + VectorMask m) { + Float16Vector that = (Float16Vector) v1; + Float16Vector tother = (Float16Vector) v2; + // It's a word: https://www.dictionary.com/browse/tother + // See also Chapter 11 of Dickens, Our Mutual Friend: + // "Totherest Governor," replied Mr Riderhood... + that.check(this); + tother.check(this); + m.check(maskClass, this); + + int opc = opCode(op); + return VectorSupport.ternaryOp( + opc, getClass(), maskClass, laneTypeOrdinal(), length(), + this, that, tother, m, + TERN_IMPL.find(op, opc, Float16Vector::ternaryOperations)); + } + + private static final + ImplCache>> + TERN_IMPL = new ImplCache<>(Ternary.class, Float16Vector.class); + + private static TernaryOperation> ternaryOperations(int opc_) { + switch (opc_) { + case VECTOR_OP_FMA: return (v0, v1_, v2_, m) -> + v0.tOp(v1_, v2_, m, (i, a, b, c) -> float16ToRawShortBits(Float16.fma(shortBitsToFloat16(a), shortBitsToFloat16(b), shortBitsToFloat16(c)))); + default: return null; + } + } + + /** + * Combines the lane values of this vector + * with the values of two broadcast scalars. + * + * This is a lane-wise ternary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, this.broadcast(e1), this.broadcast(e2))}. + * + * @param op the operation used to combine lane values + * @param e1 the first input scalar + * @param e2 the second input scalar + * @return the result of applying the operation lane-wise + * to the input vector and the scalars + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Ternary,Vector,Vector) + * @see #lanewise(VectorOperators.Ternary,short,short,VectorMask) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Ternary op, //(op,e1,e2) + short e1, + short e2) { + return lanewise(op, broadcast(e1), broadcast(e2)); + } + + /** + * Combines the lane values of this vector + * with the values of two broadcast scalars, + * with selection of lane elements controlled by a mask. + * + * This is a masked lane-wise ternary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, this.broadcast(e1), this.broadcast(e2), m)}. + * + * @param op the operation used to combine lane values + * @param e1 the first input scalar + * @param e2 the second input scalar + * @param m the mask controlling lane selection + * @return the result of applying the operation lane-wise + * to the input vector and the scalars + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Ternary,Vector,Vector,VectorMask) + * @see #lanewise(VectorOperators.Ternary,short,short) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Ternary op, //(op,e1,e2,m) + short e1, + short e2, + VectorMask m) { + return lanewise(op, broadcast(e1), broadcast(e2), m); + } + + /** + * Combines the lane values of this vector + * with the values of another vector and a broadcast scalar. + * + * This is a lane-wise ternary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, v1, this.broadcast(e2))}. + * + * @param op the operation used to combine lane values + * @param v1 the other input vector + * @param e2 the input scalar + * @return the result of applying the operation lane-wise + * to the input vectors and the scalar + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Ternary,short,short) + * @see #lanewise(VectorOperators.Ternary,Vector,short,VectorMask) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Ternary op, //(op,v1,e2) + Vector v1, + short e2) { + return lanewise(op, v1, broadcast(e2)); + } + + /** + * Combines the lane values of this vector + * with the values of another vector and a broadcast scalar, + * with selection of lane elements controlled by a mask. + * + * This is a masked lane-wise ternary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, v1, this.broadcast(e2), m)}. + * + * @param op the operation used to combine lane values + * @param v1 the other input vector + * @param e2 the input scalar + * @param m the mask controlling lane selection + * @return the result of applying the operation lane-wise + * to the input vectors and the scalar + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Ternary,Vector,Vector) + * @see #lanewise(VectorOperators.Ternary,short,short,VectorMask) + * @see #lanewise(VectorOperators.Ternary,Vector,short) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Ternary op, //(op,v1,e2,m) + Vector v1, + short e2, + VectorMask m) { + return lanewise(op, v1, broadcast(e2), m); + } + + /** + * Combines the lane values of this vector + * with the values of another vector and a broadcast scalar. + * + * This is a lane-wise ternary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, this.broadcast(e1), v2)}. + * + * @param op the operation used to combine lane values + * @param e1 the input scalar + * @param v2 the other input vector + * @return the result of applying the operation lane-wise + * to the input vectors and the scalar + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Ternary,Vector,Vector) + * @see #lanewise(VectorOperators.Ternary,short,Vector,VectorMask) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Ternary op, //(op,e1,v2) + short e1, + Vector v2) { + return lanewise(op, broadcast(e1), v2); + } + + /** + * Combines the lane values of this vector + * with the values of another vector and a broadcast scalar, + * with selection of lane elements controlled by a mask. + * + * This is a masked lane-wise ternary operation which applies + * the selected operation to each lane. + * The return value will be equal to this expression: + * {@code this.lanewise(op, this.broadcast(e1), v2, m)}. + * + * @param op the operation used to combine lane values + * @param e1 the input scalar + * @param v2 the other input vector + * @param m the mask controlling lane selection + * @return the result of applying the operation lane-wise + * to the input vectors and the scalar + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #lanewise(VectorOperators.Ternary,Vector,Vector,VectorMask) + * @see #lanewise(VectorOperators.Ternary,short,Vector) + */ + @ForceInline + public final + Float16Vector lanewise(VectorOperators.Ternary op, //(op,e1,v2,m) + short e1, + Vector v2, + VectorMask m) { + return lanewise(op, broadcast(e1), v2, m); + } + + // (Thus endeth the Great and Mighty Ternary Ogdoad.) + // https://en.wikipedia.org/wiki/Ogdoad + + /// FULL-SERVICE BINARY METHODS: ADD, SUB, MUL, DIV + // + // These include masked and non-masked versions. + // This subclass adds broadcast (masked or not). + + /** + * {@inheritDoc} + * @see #add(short) + */ + @Override + @ForceInline + public final Float16Vector add(Vector v) { + return lanewise(ADD, v); + } + + /** + * Adds this vector to the broadcast of an input scalar. + * + * This is a lane-wise binary operation which applies + * the primitive addition operation ({@code +}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short) + * lanewise}{@code (}{@link VectorOperators#ADD + * ADD}{@code , e)}. + * + * @param e the input scalar + * @return the result of adding each lane of this vector to the scalar + * @see #add(Vector) + * @see #broadcast(short) + * @see #add(short,VectorMask) + * @see VectorOperators#ADD + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final + Float16Vector add(short e) { + return lanewise(ADD, e); + } + + /** + * {@inheritDoc} + * @see #add(short,VectorMask) + */ + @Override + @ForceInline + public final Float16Vector add(Vector v, + VectorMask m) { + return lanewise(ADD, v, m); + } + + /** + * Adds this vector to the broadcast of an input scalar, + * selecting lane elements controlled by a mask. + * + * This is a masked lane-wise binary operation which applies + * the primitive addition operation ({@code +}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short,VectorMask) + * lanewise}{@code (}{@link VectorOperators#ADD + * ADD}{@code , s, m)}. + * + * @param e the input scalar + * @param m the mask controlling lane selection + * @return the result of adding each lane of this vector to the scalar + * @see #add(Vector,VectorMask) + * @see #broadcast(short) + * @see #add(short) + * @see VectorOperators#ADD + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector add(short e, + VectorMask m) { + return lanewise(ADD, e, m); + } + + /** + * {@inheritDoc} + * @see #sub(short) + */ + @Override + @ForceInline + public final Float16Vector sub(Vector v) { + return lanewise(SUB, v); + } + + /** + * Subtracts an input scalar from this vector. + * + * This is a masked lane-wise binary operation which applies + * the primitive subtraction operation ({@code -}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short) + * lanewise}{@code (}{@link VectorOperators#SUB + * SUB}{@code , e)}. + * + * @param e the input scalar + * @return the result of subtracting the scalar from each lane of this vector + * @see #sub(Vector) + * @see #broadcast(short) + * @see #sub(short,VectorMask) + * @see VectorOperators#SUB + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector sub(short e) { + return lanewise(SUB, e); + } + + /** + * {@inheritDoc} + * @see #sub(short,VectorMask) + */ + @Override + @ForceInline + public final Float16Vector sub(Vector v, + VectorMask m) { + return lanewise(SUB, v, m); + } + + /** + * Subtracts an input scalar from this vector + * under the control of a mask. + * + * This is a masked lane-wise binary operation which applies + * the primitive subtraction operation ({@code -}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short,VectorMask) + * lanewise}{@code (}{@link VectorOperators#SUB + * SUB}{@code , s, m)}. + * + * @param e the input scalar + * @param m the mask controlling lane selection + * @return the result of subtracting the scalar from each lane of this vector + * @see #sub(Vector,VectorMask) + * @see #broadcast(short) + * @see #sub(short) + * @see VectorOperators#SUB + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector sub(short e, + VectorMask m) { + return lanewise(SUB, e, m); + } + + /** + * {@inheritDoc} + * @see #mul(short) + */ + @Override + @ForceInline + public final Float16Vector mul(Vector v) { + return lanewise(MUL, v); + } + + /** + * Multiplies this vector by the broadcast of an input scalar. + * + * This is a lane-wise binary operation which applies + * the primitive multiplication operation ({@code *}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short) + * lanewise}{@code (}{@link VectorOperators#MUL + * MUL}{@code , e)}. + * + * @param e the input scalar + * @return the result of multiplying this vector by the given scalar + * @see #mul(Vector) + * @see #broadcast(short) + * @see #mul(short,VectorMask) + * @see VectorOperators#MUL + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector mul(short e) { + return lanewise(MUL, e); + } + + /** + * {@inheritDoc} + * @see #mul(short,VectorMask) + */ + @Override + @ForceInline + public final Float16Vector mul(Vector v, + VectorMask m) { + return lanewise(MUL, v, m); + } + + /** + * Multiplies this vector by the broadcast of an input scalar, + * selecting lane elements controlled by a mask. + * + * This is a masked lane-wise binary operation which applies + * the primitive multiplication operation ({@code *}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short,VectorMask) + * lanewise}{@code (}{@link VectorOperators#MUL + * MUL}{@code , s, m)}. + * + * @param e the input scalar + * @param m the mask controlling lane selection + * @return the result of muling each lane of this vector to the scalar + * @see #mul(Vector,VectorMask) + * @see #broadcast(short) + * @see #mul(short) + * @see VectorOperators#MUL + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector mul(short e, + VectorMask m) { + return lanewise(MUL, e, m); + } + + /** + * {@inheritDoc} + * @apiNote Because the underlying scalar operator is an IEEE + * floating point number, division by zero in fact will + * not throw an exception, but will yield a signed + * infinity or NaN. + */ + @Override + @ForceInline + public final Float16Vector div(Vector v) { + return lanewise(DIV, v); + } + + /** + * Divides this vector by the broadcast of an input scalar. + * + * This is a lane-wise binary operation which applies + * the primitive division operation ({@code /}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short) + * lanewise}{@code (}{@link VectorOperators#DIV + * DIV}{@code , e)}. + * + * @apiNote Because the underlying scalar operator is an IEEE + * floating point number, division by zero in fact will + * not throw an exception, but will yield a signed + * infinity or NaN. + * + * @param e the input scalar + * @return the result of dividing each lane of this vector by the scalar + * @see #div(Vector) + * @see #broadcast(short) + * @see #div(short,VectorMask) + * @see VectorOperators#DIV + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector div(short e) { + return lanewise(DIV, e); + } + + /** + * {@inheritDoc} + * @see #div(short,VectorMask) + * @apiNote Because the underlying scalar operator is an IEEE + * floating point number, division by zero in fact will + * not throw an exception, but will yield a signed + * infinity or NaN. + */ + @Override + @ForceInline + public final Float16Vector div(Vector v, + VectorMask m) { + return lanewise(DIV, v, m); + } + + /** + * Divides this vector by the broadcast of an input scalar, + * selecting lane elements controlled by a mask. + * + * This is a masked lane-wise binary operation which applies + * the primitive division operation ({@code /}) to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short,VectorMask) + * lanewise}{@code (}{@link VectorOperators#DIV + * DIV}{@code , s, m)}. + * + * @apiNote Because the underlying scalar operator is an IEEE + * floating point number, division by zero in fact will + * not throw an exception, but will yield a signed + * infinity or NaN. + * + * @param e the input scalar + * @param m the mask controlling lane selection + * @return the result of dividing each lane of this vector by the scalar + * @see #div(Vector,VectorMask) + * @see #broadcast(short) + * @see #div(short) + * @see VectorOperators#DIV + * @see #lanewise(VectorOperators.Binary,Vector) + * @see #lanewise(VectorOperators.Binary,short) + */ + @ForceInline + public final Float16Vector div(short e, + VectorMask m) { + return lanewise(DIV, e, m); + } + + /// END OF FULL-SERVICE BINARY METHODS + + /// SECOND-TIER BINARY METHODS + // + // There are no masked versions. + + /** + * {@inheritDoc} + * @apiNote + * For this method, floating point negative + * zero {@code -0.0} is treated as a value distinct from, and less + * than the default value (positive zero). + */ + @Override + @ForceInline + public final Float16Vector min(Vector v) { + return lanewise(MIN, v); + } + + // FIXME: "broadcast of an input scalar" is really wordy. Reduce? + /** + * Computes the smaller of this vector and the broadcast of an input scalar. + * + * This is a lane-wise binary operation which applies the + * operation {@code Math.min()} to each pair of + * corresponding lane values. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short) + * lanewise}{@code (}{@link VectorOperators#MIN + * MIN}{@code , e)}. + * + * @param e the input scalar + * @return the result of multiplying this vector by the given scalar + * @see #min(Vector) + * @see #broadcast(short) + * @see VectorOperators#MIN + * @see #lanewise(VectorOperators.Binary,short,VectorMask) + * @apiNote + * For this method, floating point negative + * zero {@code -0.0} is treated as a value distinct from, and less + * than the default value (positive zero). + */ + @ForceInline + public final Float16Vector min(short e) { + return lanewise(MIN, e); + } + + /** + * {@inheritDoc} + * @apiNote + * For this method, floating point negative + * zero {@code -0.0} is treated as a value distinct from, and less + * than the default value (positive zero). + */ + @Override + @ForceInline + public final Float16Vector max(Vector v) { + return lanewise(MAX, v); + } + + /** + * Computes the larger of this vector and the broadcast of an input scalar. + * + * This is a lane-wise binary operation which applies the + * operation {@code Math.max()} to each pair of + * corresponding lane values. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,short) + * lanewise}{@code (}{@link VectorOperators#MAX + * MAX}{@code , e)}. + * + * @param e the input scalar + * @return the result of multiplying this vector by the given scalar + * @see #max(Vector) + * @see #broadcast(short) + * @see VectorOperators#MAX + * @see #lanewise(VectorOperators.Binary,short,VectorMask) + * @apiNote + * For this method, floating point negative + * zero {@code -0.0} is treated as a value distinct from, and less + * than the default value (positive zero). + */ + @ForceInline + public final Float16Vector max(short e) { + return lanewise(MAX, e); + } + + + // common FP operator: pow + /** + * Raises this vector to the power of a second input vector. + * + * This is a lane-wise binary operation which applies an operation + * conforming to the specification of + * {@link Math#pow Math.pow(a,b)} + * to each pair of corresponding lane values. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,Vector) + * lanewise}{@code (}{@link VectorOperators#POW + * POW}{@code , b)}. + * + *

    + * This is not a full-service named operation like + * {@link #add(Vector) add}. A masked version of + * this operation is not directly available + * but may be obtained via the masked version of + * {@code lanewise}. + * + * @param b a vector exponent by which to raise this vector + * @return the {@code b}-th power of this vector + * @see #pow(short) + * @see VectorOperators#POW + * @see #lanewise(VectorOperators.Binary,Vector,VectorMask) + */ + @ForceInline + public final Float16Vector pow(Vector b) { + return lanewise(POW, b); + } + + /** + * Raises this vector to a scalar power. + * + * This is a lane-wise binary operation which applies an operation + * conforming to the specification of + * {@link Math#pow Math.pow(a,b)} + * to each pair of corresponding lane values. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Binary,Vector) + * lanewise}{@code (}{@link VectorOperators#POW + * POW}{@code , b)}. + * + * @param b a scalar exponent by which to raise this vector + * @return the {@code b}-th power of this vector + * @see #pow(Vector) + * @see VectorOperators#POW + * @see #lanewise(VectorOperators.Binary,short,VectorMask) + */ + @ForceInline + public final Float16Vector pow(short b) { + return lanewise(POW, b); + } + + /// UNARY METHODS + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + Float16Vector neg() { + return lanewise(NEG); + } + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + Float16Vector abs() { + return lanewise(ABS); + } + + + + // sqrt + /** + * Computes the square root of this vector. + * + * This is a lane-wise unary operation which applies an operation + * conforming to the specification of + * {@link Math#sqrt Math.sqrt(a)} + * to each lane value. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Unary) + * lanewise}{@code (}{@link VectorOperators#SQRT + * SQRT}{@code )}. + * + * @return the square root of this vector + * @see VectorOperators#SQRT + * @see #lanewise(VectorOperators.Unary,VectorMask) + */ + @ForceInline + public final Float16Vector sqrt() { + return lanewise(SQRT); + } + + /// COMPARISONS + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + VectorMask eq(Vector v) { + return compare(EQ, v); + } + + /** + * Tests if this vector is equal to an input scalar. + * + * This is a lane-wise binary test operation which applies + * the primitive equals operation ({@code ==}) to each lane. + * The result is the same as {@code compare(VectorOperators.Comparison.EQ, e)}. + * + * @param e the input scalar + * @return the result mask of testing if this vector + * is equal to {@code e} + * @see #compare(VectorOperators.Comparison,short) + */ + @ForceInline + public final + VectorMask eq(short e) { + return compare(EQ, e); + } + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + VectorMask lt(Vector v) { + return compare(LT, v); + } + + /** + * Tests if this vector is less than an input scalar. + * + * This is a lane-wise binary test operation which applies + * the primitive less than operation ({@code <}) to each lane. + * The result is the same as {@code compare(VectorOperators.LT, e)}. + * + * @param e the input scalar + * @return the mask result of testing if this vector + * is less than the input scalar + * @see #compare(VectorOperators.Comparison,short) + */ + @ForceInline + public final + VectorMask lt(short e) { + return compare(LT, e); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + VectorMask test(VectorOperators.Test op); + + /*package-private*/ + @ForceInline + final + > + M testTemplate(Class maskType, Test op) { + Float16Species vsp = vspecies(); + if (opKind(op, VO_SPECIAL)) { + ShortVector bits = this.viewAsIntegralLanes(); + VectorMask m; + if (op == IS_DEFAULT) { + m = bits.compare(EQ, (short) 0); + } else if (op == IS_NEGATIVE) { + m = bits.compare(LT, (short) 0); + } + else if (op == IS_FINITE || + op == IS_NAN || + op == IS_INFINITE) { + // first kill the sign: + bits = bits.and(Short.MAX_VALUE); + // next find the bit pattern for infinity: + short infbits = (short) toBits(float16ToRawShortBits(Float16.POSITIVE_INFINITY)); + // now compare: + if (op == IS_FINITE) { + m = bits.compare(LT, infbits); + } else if (op == IS_NAN) { + m = bits.compare(GT, infbits); + } else { + m = bits.compare(EQ, infbits); + } + } + else { + throw new AssertionError(op); + } + return maskType.cast(m.cast(vsp)); + } + int opc = opCode(op); + throw new AssertionError(op); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + VectorMask test(VectorOperators.Test op, + VectorMask m); + + /*package-private*/ + @ForceInline + final + > + M testTemplate(Class maskType, Test op, M mask) { + Float16Species vsp = vspecies(); + mask.check(maskType, this); + if (opKind(op, VO_SPECIAL)) { + ShortVector bits = this.viewAsIntegralLanes(); + VectorMask m = mask.cast(ShortVector.species(shape())); + if (op == IS_DEFAULT) { + m = bits.compare(EQ, (short) 0, m); + } else if (op == IS_NEGATIVE) { + m = bits.compare(LT, (short) 0, m); + } + else if (op == IS_FINITE || + op == IS_NAN || + op == IS_INFINITE) { + // first kill the sign: + bits = bits.and(Short.MAX_VALUE); + // next find the bit pattern for infinity: + short infbits = (short) toBits(float16ToRawShortBits(Float16.POSITIVE_INFINITY)); + // now compare: + if (op == IS_FINITE) { + m = bits.compare(LT, infbits, m); + } else if (op == IS_NAN) { + m = bits.compare(GT, infbits, m); + } else { + m = bits.compare(EQ, infbits, m); + } + } + else { + throw new AssertionError(op); + } + return maskType.cast(m.cast(vsp)); + } + int opc = opCode(op); + throw new AssertionError(op); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + VectorMask compare(VectorOperators.Comparison op, Vector v); + + /*package-private*/ + @ForceInline + final + > + M compareTemplate(Class maskType, Comparison op, Vector v) { + Float16Vector that = (Float16Vector) v; + that.check(this); + int opc = opCode(op); + return VectorSupport.compare( + opc, getClass(), maskType, laneTypeOrdinal(), length(), + this, that, null, + (cond, v0, v1, m1) -> { + AbstractMask m + = v0.bTest(cond, v1, (cond_, i, a, b) + -> compareWithOp(cond, a, b)); + @SuppressWarnings("unchecked") + M m2 = (M) m; + return m2; + }); + } + + /*package-private*/ + @ForceInline + final + > + M compareTemplate(Class maskType, Comparison op, Vector v, M m) { + Float16Vector that = (Float16Vector) v; + that.check(this); + m.check(maskType, this); + int opc = opCode(op); + return VectorSupport.compare( + opc, getClass(), maskType, laneTypeOrdinal(), length(), + this, that, m, + (cond, v0, v1, m1) -> { + AbstractMask cmpM + = v0.bTest(cond, v1, (cond_, i, a, b) + -> compareWithOp(cond, a, b)); + @SuppressWarnings("unchecked") + M m2 = (M) cmpM.and(m1); + return m2; + }); + } + + @ForceInline + private static boolean compareWithOp(int cond, short a, short b) { + return switch (cond) { + case BT_eq -> Float.float16ToFloat(a) == Float.float16ToFloat(b); + case BT_ne -> Float.float16ToFloat(a) != Float.float16ToFloat(b); + case BT_lt -> Float.float16ToFloat(a) < Float.float16ToFloat(b); + case BT_le -> Float.float16ToFloat(a) <= Float.float16ToFloat(b); + case BT_gt -> Float.float16ToFloat(a) > Float.float16ToFloat(b); + case BT_ge -> Float.float16ToFloat(a) >= Float.float16ToFloat(b); + default -> throw new AssertionError(); + }; + } + + /** + * Tests this vector by comparing it with an input scalar, + * according to the given comparison operation. + * + * This is a lane-wise binary test operation which applies + * the comparison operation to each lane. + *

    + * The result is the same as + * {@code compare(op, broadcast(species(), e))}. + * That is, the scalar may be regarded as broadcast to + * a vector of the same species, and then compared + * against the original vector, using the selected + * comparison operation. + * + * @param op the operation used to compare lane values + * @param e the input scalar + * @return the mask result of testing lane-wise if this vector + * compares to the input, according to the selected + * comparison operator + * @see Float16Vector#compare(VectorOperators.Comparison,Vector) + * @see #eq(short) + * @see #lt(short) + */ + public abstract + VectorMask compare(Comparison op, short e); + + /*package-private*/ + @ForceInline + final + > + M compareTemplate(Class maskType, Comparison op, short e) { + return compareTemplate(maskType, op, broadcast(e)); + } + + /** + * Tests this vector by comparing it with an input scalar, + * according to the given comparison operation, + * in lanes selected by a mask. + * + * This is a masked lane-wise binary test operation which applies + * to each pair of corresponding lane values. + * + * The returned result is equal to the expression + * {@code compare(op,s).and(m)}. + * + * @param op the operation used to compare lane values + * @param e the input scalar + * @param m the mask controlling lane selection + * @return the mask result of testing lane-wise if this vector + * compares to the input, according to the selected + * comparison operator, + * and only in the lanes selected by the mask + * @see Float16Vector#compare(VectorOperators.Comparison,Vector,VectorMask) + */ + @ForceInline + public final VectorMask compare(VectorOperators.Comparison op, + short e, + VectorMask m) { + return compare(op, broadcast(e), m); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + VectorMask compare(Comparison op, long e); + + /*package-private*/ + @ForceInline + final + > + M compareTemplate(Class maskType, Comparison op, long e) { + return compareTemplate(maskType, op, broadcast(e)); + } + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + VectorMask compare(Comparison op, long e, VectorMask m) { + return compare(op, broadcast(e), m); + } + + + + /** + * {@inheritDoc} + */ + @Override public abstract + Float16Vector blend(Vector v, VectorMask m); + + /*package-private*/ + @ForceInline + final + > + Float16Vector + blendTemplate(Class maskType, Float16Vector v, M m) { + v.check(this); + return VectorSupport.blend( + getClass(), maskType, laneTypeOrdinal(), length(), + this, v, m, + (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); + } + + /** + * {@inheritDoc} + */ + @Override public abstract Float16Vector addIndex(int scale); + + /*package-private*/ + @ForceInline + final Float16Vector addIndexTemplate(int scale) { + Float16Species vsp = vspecies(); + // make sure VLENGTH*scale doesn't overflow: + vsp.checkScale(scale); + return VectorSupport.indexVector( + getClass(), laneTypeOrdinal(), length(), + this, scale, vsp, + (v, scale_, s) + -> { + // If the platform doesn't support an INDEX + // instruction directly, load IOTA from memory + // and multiply. + Float16Vector iota = s.iota(); + short sc = (short) scale_; + return v.add(sc == 1 ? iota : iota.mul(float16ToRawShortBits(Float16.valueOf(sc)))); + }); + } + + /** + * Replaces selected lanes of this vector with + * a scalar value + * under the control of a mask. + * + * This is a masked lane-wise binary operation which + * selects each lane value from one or the other input. + * + * The returned result is equal to the expression + * {@code blend(broadcast(e),m)}. + * + * @param e the input scalar, containing the replacement lane value + * @param m the mask controlling lane selection of the scalar + * @return the result of blending the lane elements of this vector with + * the scalar value + */ + @ForceInline + public final Float16Vector blend(short e, + VectorMask m) { + return blend(broadcast(e), m); + } + + /** + * Replaces selected lanes of this vector with + * a scalar value + * under the control of a mask. + * + * This is a masked lane-wise binary operation which + * selects each lane value from one or the other input. + * + * The returned result is equal to the expression + * {@code blend(broadcast(e),m)}. + * + * @param e the input scalar, containing the replacement lane value + * @param m the mask controlling lane selection of the scalar + * @return the result of blending the lane elements of this vector with + * the scalar value + */ + @ForceInline + public final Float16Vector blend(long e, + VectorMask m) { + return blend(broadcast(e), m); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector slice(int origin, Vector v1); + + /*package-private*/ + final + @ForceInline + Float16Vector sliceTemplate(int origin, Vector v1) { + Float16Vector that = (Float16Vector) v1; + that.check(this); + Objects.checkIndex(origin, length() + 1); + ShortVector iotaVector = (ShortVector) iotaShuffle().toBitsVector(); + ShortVector filter = ShortVector.broadcast((ShortVector.ShortSpecies) vspecies().asIntegral(), (short)(length() - origin)); + VectorMask blendMask = iotaVector.compare(VectorOperators.LT, filter).cast(vspecies()); + AbstractShuffle iota = iotaShuffle(origin, 1, true); + return that.rearrange(iota).blend(this.rearrange(iota), blendMask); + } + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + Float16Vector slice(int origin, + Vector w, + VectorMask m) { + return broadcast(0).blend(slice(origin, w), m); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector slice(int origin); + + /*package-private*/ + final + @ForceInline + Float16Vector sliceTemplate(int origin) { + Objects.checkIndex(origin, length() + 1); + ShortVector iotaVector = (ShortVector) iotaShuffle().toBitsVector(); + ShortVector filter = ShortVector.broadcast((ShortVector.ShortSpecies) vspecies().asIntegral(), (short)(length() - origin)); + VectorMask blendMask = iotaVector.compare(VectorOperators.LT, filter).cast(vspecies()); + AbstractShuffle iota = iotaShuffle(origin, 1, true); + return vspecies().zero().blend(this.rearrange(iota), blendMask); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector unslice(int origin, Vector w, int part); + + /*package-private*/ + final + @ForceInline + Float16Vector + unsliceTemplate(int origin, Vector w, int part) { + Float16Vector that = (Float16Vector) w; + that.check(this); + Objects.checkIndex(origin, length() + 1); + if ((-2 & part) != 0) { + throw wrongPartForSlice(part); + } + ShortVector iotaVector = (ShortVector) iotaShuffle().toBitsVector(); + ShortVector filter = ShortVector.broadcast((ShortVector.ShortSpecies) vspecies().asIntegral(), (short)origin); + VectorMask blendMask = iotaVector.compare((part == 0) ? VectorOperators.GE : VectorOperators.LT, filter).cast(vspecies()); + AbstractShuffle iota = iotaShuffle(-origin, 1, true); + return that.blend(this.rearrange(iota), blendMask); + } + + /*package-private*/ + final + @ForceInline + > + Float16Vector + unsliceTemplate(Class maskType, int origin, Vector w, int part, M m) { + Float16Vector that = (Float16Vector) w; + that.check(this); + Float16Vector slice = that.sliceTemplate(origin, that); + slice = slice.blendTemplate(maskType, this, m); + return slice.unsliceTemplate(origin, w, part); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector unslice(int origin, Vector w, int part, VectorMask m); + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector unslice(int origin); + + /*package-private*/ + final + @ForceInline + Float16Vector + unsliceTemplate(int origin) { + Objects.checkIndex(origin, length() + 1); + ShortVector iotaVector = (ShortVector) iotaShuffle().toBitsVector(); + ShortVector filter = ShortVector.broadcast((ShortVector.ShortSpecies) vspecies().asIntegral(), (short)origin); + VectorMask blendMask = iotaVector.compare(VectorOperators.GE, filter).cast(vspecies()); + AbstractShuffle iota = iotaShuffle(-origin, 1, true); + return vspecies().zero().blend(this.rearrange(iota), blendMask); + } + + private ArrayIndexOutOfBoundsException + wrongPartForSlice(int part) { + String msg = String.format("bad part number %d for slice operation", + part); + return new ArrayIndexOutOfBoundsException(msg); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector rearrange(VectorShuffle shuffle); + + /*package-private*/ + @ForceInline + final + > + Float16Vector rearrangeTemplate(Class shuffletype, S shuffle) { + Objects.requireNonNull(shuffle); + return VectorSupport.rearrangeOp( + getClass(), shuffletype, null, laneTypeOrdinal(), length(), + this, shuffle, null, + (v1, s_, m_) -> v1.uRawOp((i, a) -> { + int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); + return v1.lane(ei); + })); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector rearrange(VectorShuffle s, + VectorMask m); + + /*package-private*/ + @ForceInline + final + , M extends VectorMask> + Float16Vector rearrangeTemplate(Class shuffletype, + Class masktype, + S shuffle, + M m) { + Objects.requireNonNull(shuffle); + m.check(masktype, this); + return VectorSupport.rearrangeOp( + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), + this, shuffle, m, + (v1, s_, m_) -> v1.uRawOp((i, a) -> { + int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); + })); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector rearrange(VectorShuffle s, + Vector v); + + /*package-private*/ + @ForceInline + final + > + Float16Vector rearrangeTemplate(Class shuffletype, + S shuffle, + Float16Vector v) { + VectorMask valid = shuffle.laneIsValid(); + Float16Vector r0 = + VectorSupport.rearrangeOp( + getClass(), shuffletype, null, laneTypeOrdinal(), length(), + this, shuffle, null, + (v0, s_, m_) -> v0.uRawOp((i, a) -> { + int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); + return v0.lane(ei); + })); + Float16Vector r1 = + VectorSupport.rearrangeOp( + getClass(), shuffletype, null, laneTypeOrdinal(), length(), + v, shuffle, null, + (v1, s_, m_) -> v1.uRawOp((i, a) -> { + int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); + return v1.lane(ei); + })); + return r1.blend(r0, valid); + } + + @Override + @ForceInline + final VectorShuffle bitsToShuffle0(AbstractSpecies dsp) { + throw new AssertionError(); + } + + @ForceInline + final + VectorShuffle toShuffle(AbstractSpecies dsp, boolean wrap) { + assert(dsp.elementSize() == vspecies().elementSize()); + ShortVector idx = convert(VectorOperators.H2S, 0).reinterpretAsShorts(); + ShortVector wrapped = idx.lanewise(VectorOperators.AND, length() - 1); + if (!wrap) { + ShortVector wrappedEx = wrapped.lanewise(VectorOperators.SUB, length()); + VectorMask inBound = wrapped.compare(VectorOperators.EQ, idx); + wrapped = wrappedEx.blend(wrapped, inBound); + } + return wrapped.bitsToShuffle(dsp); + } + + /** + * {@inheritDoc} + * @since 27 + */ + @Override + public abstract + Float16Vector compress(VectorMask m); + + /*package-private*/ + @ForceInline + final + > + Float16Vector compressTemplate(Class masktype, M m) { + m.check(masktype, this); + return (Float16Vector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, + laneTypeOrdinal(), length(), this, m, + (v1, m1) -> compressHelper(v1, m1)); + } + + /** + * {@inheritDoc} + * @since 27 + */ + @Override + public abstract + Float16Vector expand(VectorMask m); + + /*package-private*/ + @ForceInline + final + > + Float16Vector expandTemplate(Class masktype, M m) { + m.check(masktype, this); + return (Float16Vector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, + laneTypeOrdinal(), length(), this, m, + (v1, m1) -> expandHelper(v1, m1)); + } + + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector selectFrom(Vector v); + + /*package-private*/ + @ForceInline + final Float16Vector selectFromTemplate(Float16Vector v) { + return (Float16Vector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); + } + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector selectFrom(Vector s, VectorMask m); + + /*package-private*/ + @ForceInline + final + > + Float16Vector selectFromTemplate(Float16Vector v, + Class masktype, M m) { + m.check(masktype, this); + return (Float16Vector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); + } + + + /** + * {@inheritDoc} + */ + @Override + public abstract + Float16Vector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final Float16Vector selectFromTemplate(Float16Vector v1, Float16Vector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + + /// Ternary operations + + + /** + * Multiplies this vector by a second input vector, and sums + * the result with a third. + * + * Extended precision is used for the intermediate result, + * avoiding possible loss of precision from rounding once + * for each of the two operations. + * The result is numerically close to {@code this.mul(b).add(c)}, + * and is typically closer to the true mathematical result. + * + * This is a lane-wise ternary operation which applies an operation + * conforming to the specification of + * {@link Math#fma(float,float,float) Math.fma(a,b,c)} + * to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Ternary,Vector,Vector) + * lanewise}{@code (}{@link VectorOperators#FMA + * FMA}{@code , b, c)}. + * + * @param b the second input vector, supplying multiplier values + * @param c the third input vector, supplying addend values + * @return the product of this vector and the second input vector + * summed with the third input vector, using extended precision + * for the intermediate result + * @see #fma(short,short) + * @see VectorOperators#FMA + * @see #lanewise(VectorOperators.Ternary,Vector,Vector,VectorMask) + */ + @ForceInline + public final + Float16Vector fma(Vector b, Vector c) { + return lanewise(FMA, b, c); + } + + /** + * Multiplies this vector by a scalar multiplier, and sums + * the result with a scalar addend. + * + * Extended precision is used for the intermediate result, + * avoiding possible loss of precision from rounding once + * for each of the two operations. + * The result is numerically close to {@code this.mul(b).add(c)}, + * and is typically closer to the true mathematical result. + * + * This is a lane-wise ternary operation which applies an operation + * conforming to the specification of + * {@link Math#fma(float,float,float) Math.fma(a,b,c)} + * to each lane. + * + * This method is also equivalent to the expression + * {@link #lanewise(VectorOperators.Ternary,Vector,Vector) + * lanewise}{@code (}{@link VectorOperators#FMA + * FMA}{@code , b, c)}. + * + * @param b the scalar multiplier + * @param c the scalar addend + * @return the product of this vector and the scalar multiplier + * summed with scalar addend, using extended precision + * for the intermediate result + * @see #fma(Vector,Vector) + * @see VectorOperators#FMA + * @see #lanewise(VectorOperators.Ternary,short,short,VectorMask) + */ + @ForceInline + public final + Float16Vector fma(short b, short c) { + return lanewise(FMA, b, c); + } + + // Don't bother with (Vector,short) and (short,Vector) overloadings. + + // Type specific horizontal reductions + + /** + * Returns a value accumulated from all the lanes of this vector. + * + * This is an associative cross-lane reduction operation which + * applies the specified operation to all the lane elements. + *

    + * A few reduction operations do not support arbitrary reordering + * of their operands, yet are included here because of their + * usefulness. + *

      + *
    • + * In the case of {@code FIRST_NONZERO}, the reduction returns + * the value from the lowest-numbered non-zero lane. + * (As with {@code MAX} and {@code MIN}, floating point negative + * zero {@code -0.0} is treated as a value distinct from + * the default value, positive zero. So a first-nonzero lane reduction + * might return {@code -0.0} even in the presence of non-zero + * lane values.) + *
    • + * In the case of {@code ADD} and {@code MUL}, the + * precise result will reflect the choice of an arbitrary order + * of operations, which may even vary over time. + * For further details see the section + * Operations on floating point vectors. + *
    • + * All other reduction operations are fully commutative and + * associative. The implementation can choose any order of + * processing, yet it will always produce the same result. + *
    + * + * @param op the operation used to combine lane values + * @return the accumulated result + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #reduceLanes(VectorOperators.Associative,VectorMask) + * @see #add(Vector) + * @see #mul(Vector) + * @see #min(Vector) + * @see #max(Vector) + * @see VectorOperators#FIRST_NONZERO + */ + public abstract short reduceLanes(VectorOperators.Associative op); + + /** + * Returns a value accumulated from selected lanes of this vector, + * controlled by a mask. + * + * This is an associative cross-lane reduction operation which + * applies the specified operation to the selected lane elements. + *

    + * If no elements are selected, an operation-specific identity + * value is returned. + *

      + *
    • + * If the operation is + * {@code ADD} + * or {@code FIRST_NONZERO}, + * then the identity value is positive zero, the default {@code short} value. + *
    • + * If the operation is {@code MUL}, + * then the identity value is one. + *
    • + * If the operation is {@code MAX}, + * then the identity value is {@code Float16.NEGATIVE_INFINITY}. + *
    • + * If the operation is {@code MIN}, + * then the identity value is {@code Float16.POSITIVE_INFINITY}. + *
    + *

    + * A few reduction operations do not support arbitrary reordering + * of their operands, yet are included here because of their + * usefulness. + *

      + *
    • + * In the case of {@code FIRST_NONZERO}, the reduction returns + * the value from the lowest-numbered non-zero lane. + * (As with {@code MAX} and {@code MIN}, floating point negative + * zero {@code -0.0} is treated as a value distinct from + * the default value, positive zero. So a first-nonzero lane reduction + * might return {@code -0.0} even in the presence of non-zero + * lane values.) + *
    • + * In the case of {@code ADD} and {@code MUL}, the + * precise result will reflect the choice of an arbitrary order + * of operations, which may even vary over time. + * For further details see the section + * Operations on floating point vectors. + *
    • + * All other reduction operations are fully commutative and + * associative. The implementation can choose any order of + * processing, yet it will always produce the same result. + *
    + * + * @param op the operation used to combine lane values + * @param m the mask controlling lane selection + * @return the reduced result accumulated from the selected lane values + * @throws UnsupportedOperationException if this vector does + * not support the requested operation + * @see #reduceLanes(VectorOperators.Associative) + */ + public abstract short reduceLanes(VectorOperators.Associative op, + VectorMask m); + + /*package-private*/ + @ForceInline + final + short reduceLanesTemplate(VectorOperators.Associative op, + Class> maskClass, + VectorMask m) { + m.check(maskClass, this); + if (op == FIRST_NONZERO) { + // FIXME: The JIT should handle this. + Float16Vector v = broadcast((short) 0).blend(this, m); + return v.reduceLanesTemplate(op); + } + int opc = opCode(op); + return fromBits(VectorSupport.reductionCoerced( + opc, getClass(), maskClass, laneTypeOrdinal(), length(), + this, m, + REDUCE_IMPL.find(op, opc, Float16Vector::reductionOperations))); + } + + /*package-private*/ + @ForceInline + final + short reduceLanesTemplate(VectorOperators.Associative op) { + if (op == FIRST_NONZERO) { + // FIXME: The JIT should handle this. + VectorMask thisNZ + = this.viewAsIntegralLanes().compare(NE, (short) 0); + int ft = thisNZ.firstTrue(); + return ft < length() ? this.lane(ft) : (short) 0; + } + int opc = opCode(op); + return fromBits(VectorSupport.reductionCoerced( + opc, getClass(), null, laneTypeOrdinal(), length(), + this, null, + REDUCE_IMPL.find(op, opc, Float16Vector::reductionOperations))); + } + + private static final + ImplCache>> + REDUCE_IMPL = new ImplCache<>(Associative.class, Float16Vector.class); + + private static ReductionOperation> reductionOperations(int opc_) { + switch (opc_) { + case VECTOR_OP_ADD: return (v, m) -> + toBits(v.rOp((short)0, m, (i, a, b) -> (float)(a + b))); + case VECTOR_OP_MUL: return (v, m) -> + toBits(v.rOp((short)floatToFloat16(1.0f), m, (i, a, b) -> (float)(a * b))); + case VECTOR_OP_MIN: return (v, m) -> + toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (float) Math.min(a, b))); + case VECTOR_OP_MAX: return (v, m) -> + toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (float) Math.max(a, b))); + default: return null; + } + } + + private static final short MIN_OR_INF = float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + private static final short MAX_OR_INF = float16ToRawShortBits(Float16.POSITIVE_INFINITY); + + public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); + public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, + VectorMask m); + + // Type specific accessors + + /** + * Gets the lane element at lane index {@code i} + * + * @param i the lane index + * @return the lane element at lane index {@code i} + * @throws IllegalArgumentException if the index is out of range + * ({@code < 0 || >= length()}) + */ + public abstract short lane(int i); + + /** + * Replaces the lane element of this vector at lane index {@code i} with + * value {@code e}. + * + * This is a cross-lane operation and behaves as if it returns the result + * of blending this vector with an input vector that is the result of + * broadcasting {@code e} and a mask that has only one lane set at lane + * index {@code i}. + * + * @param i the lane index of the lane element to be replaced + * @param e the value to be placed + * @return the result of replacing the lane element of this vector at lane + * index {@code i} with value {@code e}. + * @throws IllegalArgumentException if the index is out of range + * ({@code < 0 || >= length()}) + */ + public abstract Float16Vector withLane(int i, short e); + + // Memory load operations + + /** + * Returns an array of type {@code short[]} + * containing all the lane values. + * The array length is the same as the vector length. + * The array elements are stored in lane order. + *

    + * This method behaves as if it stores + * this vector into an allocated array + * (using {@link #intoArray(short[], int) intoArray}) + * and returns the array as follows: + *

    {@code
    +     *   short[] a = new short[this.length()];
    +     *   this.intoArray(a, 0);
    +     *   return a;
    +     * }
    + * + * @return an array containing the lane values of this vector + */ + @ForceInline + @Override + public final short[] toArray() { + short[] a = new short[vspecies().laneCount()]; + intoArray(a, 0); + return a; + } + + // Returns the lane values boxed as Float16 elements. + @ForceInline + final Float16[] toFloat16Array() { + short[] bits = vec(); + Float16[] a = new Float16[bits.length]; + for (int i = 0; i < bits.length; i++) { + a[i] = Float16.shortBitsToFloat16(bits[i]); + } + return a; + } + + /** {@inheritDoc} + */ + @ForceInline + @Override + public final int[] toIntArray() { + short[] a = toArray(); + int[] res = new int[a.length]; + for (int i = 0; i < a.length; i++) { + short e = a[i]; + res[i] = (int) Float16Species.toIntegralChecked(e, true); + } + return res; + } + + /** {@inheritDoc} + */ + @ForceInline + @Override + public final long[] toLongArray() { + short[] a = toArray(); + long[] res = new long[a.length]; + for (int i = 0; i < a.length; i++) { + short e = a[i]; + res[i] = Float16Species.toIntegralChecked(e, false); + } + return res; + } + + /** {@inheritDoc} + * @implNote + * When this method is used on vectors + * of type {@code Float16Vector}, + * there will be no loss of precision. + */ + @ForceInline + @Override + public final double[] toDoubleArray() { + short[] a = toArray(); + double[] res = new double[a.length]; + for (int i = 0; i < a.length; i++) { + res[i] = (double) shortBitsToFloat16(a[i]).doubleValue(); + } + return res; + } + + /** + * Loads a vector from an array of type {@code short[]} holding IEEE 754 binary16 values + * starting at an offset. + * For each vector lane, where {@code N} is the vector lane index, the + * array element at index {@code offset + N} is placed into the + * resulting vector at lane index {@code N}. + * + * @param species species of desired vector + * @param a the array + * @param offset the offset into the array + * @return the vector loaded from an array + * @throws IndexOutOfBoundsException + * if {@code offset+N < 0} or {@code offset+N >= a.length} + * for any lane {@code N} in the vector + */ + @ForceInline + public static + Float16Vector fromArray(VectorSpecies species, + short[] a, int offset) { + offset = checkFromIndexSize(offset, species.length(), a.length); + Float16Species vsp = (Float16Species) species; + return vsp.dummyVector().fromArray0(a, offset); + } + + /** + * Loads a vector from an array of type {@code short[]} holding IEEE 754 binary16 values + * starting at an offset and using a mask. + * Lanes where the mask is unset are filled with the default + * value of {@code short} (positive zero). + * For each vector lane, where {@code N} is the vector lane index, + * if the mask lane at index {@code N} is set then the array element at + * index {@code offset + N} is placed into the resulting vector at lane index + * {@code N}, otherwise the default element value is placed into the + * resulting vector at lane index {@code N}. + * + * @param species species of desired vector + * @param a the array + * @param offset the offset into the array + * @param m the mask controlling lane selection + * @return the vector loaded from an array + * @throws IndexOutOfBoundsException + * if {@code offset+N < 0} or {@code offset+N >= a.length} + * for any lane {@code N} in the vector + * where the mask is set + */ + @ForceInline + public static + Float16Vector fromArray(VectorSpecies species, + short[] a, int offset, + VectorMask m) { + Float16Species vsp = (Float16Species) species; + if (VectorIntrinsics.indexInRange(offset, vsp.length(), a.length)) { + return vsp.dummyVector().fromArray0(a, offset, m, OFFSET_IN_RANGE); + } + + ((AbstractMask)m) + .checkIndexByLane(offset, a.length, vsp.iota(), 1); + return vsp.dummyVector().fromArray0(a, offset, m, OFFSET_OUT_OF_RANGE); + } + + /** + * Gathers a new vector composed of elements from an array of type + * {@code short[]} holding IEEE 754 binary16 values, + * using indexes obtained by adding a fixed {@code offset} to a + * series of secondary offsets from an index map. + * The index map is a contiguous sequence of {@code VLENGTH} + * elements in a second array of {@code int}s, starting at a given + * {@code mapOffset}. + *

    + * For each vector lane, where {@code N} is the vector lane index, + * the lane is loaded from the array + * element {@code a[f(N)]}, where {@code f(N)} is the + * index mapping expression + * {@code offset + indexMap[mapOffset + N]]}. + * + * @param species species of desired vector + * @param a the array + * @param offset the offset into the array, may be negative if relative + * indexes in the index map compensate to produce a value within the + * array bounds + * @param indexMap the index map + * @param mapOffset the offset into the index map + * @return the vector loaded from the indexed elements of the array + * @throws IndexOutOfBoundsException + * if {@code mapOffset+N < 0} + * or if {@code mapOffset+N >= indexMap.length}, + * or if {@code f(N)=offset+indexMap[mapOffset+N]} + * is an invalid index into {@code a}, + * for any lane {@code N} in the vector + * @see Float16Vector#toIntArray() + */ + @ForceInline + public static + Float16Vector fromArray(VectorSpecies species, + short[] a, int offset, + int[] indexMap, int mapOffset) { + Float16Species vsp = (Float16Species) species; + IntVector.IntSpecies isp = IntVector.species(vsp.indexShape()); + Objects.requireNonNull(a); + Objects.requireNonNull(indexMap); + Class vectorType = vsp.vectorType(); + + + // Constant folding should sweep out following conditonal logic. + VectorSpecies lsp; + if (isp.length() > IntVector.SPECIES_PREFERRED.length()) { + lsp = IntVector.SPECIES_PREFERRED; + } else { + lsp = isp; + } + + // Check indices are within array bounds. + IntVector vix0 = IntVector.fromArray(lsp, indexMap, mapOffset).add(offset); + VectorIntrinsics.checkIndex(vix0, a.length); + + int vlen = vsp.length(); + int idx_vlen = lsp.length(); + IntVector vix1 = null; + if (vlen >= idx_vlen * 2) { + vix1 = IntVector.fromArray(lsp, indexMap, mapOffset + idx_vlen).add(offset); + VectorIntrinsics.checkIndex(vix1, a.length); + } + + return VectorSupport.loadWithMap( + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), + lsp.vectorType(), lsp.length(), + a, ARRAY_BASE, vix0, vix1, null, null, null, + a, offset, indexMap, mapOffset, vsp, + (c, idx, iMap, idy, s, vm) -> + s.vOp(n -> c[idx + iMap[idy+n]])); + } + + /** + * Gathers a new vector composed of elements from an array of type + * {@code short[]} holding IEEE 754 binary16 values, + * under the control of a mask, and + * using indexes obtained by adding a fixed {@code offset} to a + * series of secondary offsets from an index map. + * The index map is a contiguous sequence of {@code VLENGTH} + * elements in a second array of {@code int}s, starting at a given + * {@code mapOffset}. + *

    + * For each vector lane, where {@code N} is the vector lane index, + * if the lane is set in the mask, + * the lane is loaded from the array + * element {@code a[f(N)]}, where {@code f(N)} is the + * index mapping expression + * {@code offset + indexMap[mapOffset + N]]}. + * Unset lanes in the resulting vector are set to zero. + * + * @param species species of desired vector + * @param a the array + * @param offset the offset into the array, may be negative if relative + * indexes in the index map compensate to produce a value within the + * array bounds + * @param indexMap the index map + * @param mapOffset the offset into the index map + * @param m the mask controlling lane selection + * @return the vector loaded from the indexed elements of the array + * @throws IndexOutOfBoundsException + * if {@code mapOffset+N < 0} + * or if {@code mapOffset+N >= indexMap.length}, + * or if {@code f(N)=offset+indexMap[mapOffset+N]} + * is an invalid index into {@code a}, + * for any lane {@code N} in the vector + * where the mask is set + * @see Float16Vector#toIntArray() + */ + @ForceInline + public static + Float16Vector fromArray(VectorSpecies species, + short[] a, int offset, + int[] indexMap, int mapOffset, + VectorMask m) { + if (m.allTrue()) { + return fromArray(species, a, offset, indexMap, mapOffset); + } + else { + Float16Species vsp = (Float16Species) species; + return vsp.dummyVector().fromArray0(a, offset, indexMap, mapOffset, m); + } + } + + + + /** + * Loads a vector from a {@linkplain MemorySegment memory segment} + * starting at an offset into the memory segment. + * Bytes are composed into primitive lane elements according + * to the specified byte order. + * The vector is arranged into lanes according to + * memory ordering. + *

    + * This method behaves as if it returns the result of calling + * {@link #fromMemorySegment(VectorSpecies,MemorySegment,long,ByteOrder,VectorMask) + * fromMemorySegment()} as follows: + *

    {@code
    +     * var m = species.maskAll(true);
    +     * return fromMemorySegment(species, ms, offset, bo, m);
    +     * }
    + * + * @param species species of desired vector + * @param ms the memory segment + * @param offset the offset into the memory segment + * @param bo the intended byte order + * @return a vector loaded from the memory segment + * @throws IndexOutOfBoundsException + * if {@code offset+N*2 < 0} + * or {@code offset+N*2 >= ms.byteSize()} + * for any lane {@code N} in the vector + * @throws IllegalStateException if the memory segment's session is not alive, + * or if access occurs from a thread other than the thread owning the session. + * @since 27 + */ + @ForceInline + public static + Float16Vector fromMemorySegment(VectorSpecies species, + MemorySegment ms, long offset, + ByteOrder bo) { + offset = checkFromIndexSize(offset, species.vectorByteSize(), ms.byteSize()); + Float16Species vsp = (Float16Species) species; + return vsp.dummyVector().fromMemorySegment0(ms, offset).maybeSwap(bo); + } + + /** + * Loads a vector from a {@linkplain MemorySegment memory segment} + * starting at an offset into the memory segment + * and using a mask. + * Lanes where the mask is unset are filled with the default + * value of {@code short} (positive zero). + * Bytes are composed into primitive lane elements according + * to the specified byte order. + * The vector is arranged into lanes according to + * memory ordering. + *

    + * The following pseudocode illustrates the behavior: + *

    {@code
    +     * var slice = ms.asSlice(offset);
    +     * short[] ar = new short[species.length()];
    +     * for (int n = 0; n < ar.length; n++) {
    +     *     if (m.laneIsSet(n)) {
    +     *         ar[n] = slice.getAtIndex(ValuaLayout.JAVA_SHORT.withByteAlignment(1), n);
    +     *     }
    +     * }
    +     * Float16Vector r = Float16Vector.fromArray(species, ar, 0);
    +     * }
    + * @implNote + * This operation is likely to be more efficient if + * the specified byte order is the same as + * {@linkplain ByteOrder#nativeOrder() + * the platform native order}, + * since this method will not need to reorder + * the bytes of lane values. + * + * @param species species of desired vector + * @param ms the memory segment + * @param offset the offset into the memory segment + * @param bo the intended byte order + * @param m the mask controlling lane selection + * @return a vector loaded from the memory segment + * @throws IndexOutOfBoundsException + * if {@code offset+N*2 < 0} + * or {@code offset+N*2 >= ms.byteSize()} + * for any lane {@code N} in the vector + * where the mask is set + * @throws IllegalStateException if the memory segment's session is not alive, + * or if access occurs from a thread other than the thread owning the session. + * @since 27 + */ + @ForceInline + public static + Float16Vector fromMemorySegment(VectorSpecies species, + MemorySegment ms, long offset, + ByteOrder bo, + VectorMask m) { + Float16Species vsp = (Float16Species) species; + if (VectorIntrinsics.indexInRange(offset, vsp.vectorByteSize(), ms.byteSize())) { + return vsp.dummyVector().fromMemorySegment0(ms, offset, m, OFFSET_IN_RANGE).maybeSwap(bo); + } + + ((AbstractMask)m) + .checkIndexByLane(offset, ms.byteSize(), vsp.iota(), 2); + return vsp.dummyVector().fromMemorySegment0(ms, offset, m, OFFSET_OUT_OF_RANGE).maybeSwap(bo); + } + + // Memory store operations + + /** + * Stores this vector into an array of type {@code short[]} + * starting at an offset. + *

    + * For each vector lane, where {@code N} is the vector lane index, + * the lane element at index {@code N} is stored into the array + * element {@code a[offset+N]}. + * + * @param a the array, of type {@code short[]} + * @param offset the offset into the array + * @throws IndexOutOfBoundsException + * if {@code offset+N < 0} or {@code offset+N >= a.length} + * for any lane {@code N} in the vector + */ + @ForceInline + public final + void intoArray(short[] a, int offset) { + offset = checkFromIndexSize(offset, length(), a.length); + Float16Species vsp = vspecies(); + VectorSupport.store( + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), + a, arrayAddress(a, offset), false, + this, + a, offset, + (arr, off, v) + -> v.stOp(arr, (int) off, + (arr_, off_, i, e) -> arr_[off_ + i] = e)); + } + + /** + * Stores this vector into an array of type {@code short[]} + * starting at offset and using a mask. + *

    + * For each vector lane, where {@code N} is the vector lane index, + * the lane element at index {@code N} is stored into the array + * element {@code a[offset+N]}. + * If the mask lane at {@code N} is unset then the corresponding + * array element {@code a[offset+N]} is left unchanged. + *

    + * Array range checking is done for lanes where the mask is set. + * Lanes where the mask is unset are not stored and do not need + * to correspond to legitimate elements of {@code a}. + * That is, unset lanes may correspond to array indexes less than + * zero or beyond the end of the array. + * + * @param a the array, of type {@code short[]} + * @param offset the offset into the array + * @param m the mask controlling lane storage + * @throws IndexOutOfBoundsException + * if {@code offset+N < 0} or {@code offset+N >= a.length} + * for any lane {@code N} in the vector + * where the mask is set + */ + @ForceInline + public final + void intoArray(short[] a, int offset, + VectorMask m) { + if (m.allTrue()) { + intoArray(a, offset); + } else { + Float16Species vsp = vspecies(); + if (!VectorIntrinsics.indexInRange(offset, vsp.length(), a.length)) { + ((AbstractMask)m) + .checkIndexByLane(offset, a.length, vsp.iota(), 1); + } + intoArray0(a, offset, m); + } + } + + /** + * Scatters this vector into an array of type {@code short[]} + * using indexes obtained by adding a fixed {@code offset} to a + * series of secondary offsets from an index map. + * The index map is a contiguous sequence of {@code VLENGTH} + * elements in a second array of {@code int}s, starting at a given + * {@code mapOffset}. + *

    + * For each vector lane, where {@code N} is the vector lane index, + * the lane element at index {@code N} is stored into the array + * element {@code a[f(N)]}, where {@code f(N)} is the + * index mapping expression + * {@code offset + indexMap[mapOffset + N]]}. + * + * @param a the array + * @param offset an offset to combine with the index map offsets + * @param indexMap the index map + * @param mapOffset the offset into the index map + * @throws IndexOutOfBoundsException + * if {@code mapOffset+N < 0} + * or if {@code mapOffset+N >= indexMap.length}, + * or if {@code f(N)=offset+indexMap[mapOffset+N]} + * is an invalid index into {@code a}, + * for any lane {@code N} in the vector + * @see Float16Vector#toIntArray() + */ + @ForceInline + public final + void intoArray(short[] a, int offset, + int[] indexMap, int mapOffset) { + stOp(a, offset, + (arr, off, i, e) -> { + int j = indexMap[mapOffset + i]; + arr[off + j] = e; + }); + } + + /** + * Scatters this vector into an array of type {@code short[]}, + * under the control of a mask, and + * using indexes obtained by adding a fixed {@code offset} to a + * series of secondary offsets from an index map. + * The index map is a contiguous sequence of {@code VLENGTH} + * elements in a second array of {@code int}s, starting at a given + * {@code mapOffset}. + *

    + * For each vector lane, where {@code N} is the vector lane index, + * if the mask lane at index {@code N} is set then + * the lane element at index {@code N} is stored into the array + * element {@code a[f(N)]}, where {@code f(N)} is the + * index mapping expression + * {@code offset + indexMap[mapOffset + N]]}. + * + * @param a the array + * @param offset an offset to combine with the index map offsets + * @param indexMap the index map + * @param mapOffset the offset into the index map + * @param m the mask + * @throws IndexOutOfBoundsException + * if {@code mapOffset+N < 0} + * or if {@code mapOffset+N >= indexMap.length}, + * or if {@code f(N)=offset+indexMap[mapOffset+N]} + * is an invalid index into {@code a}, + * for any lane {@code N} in the vector + * where the mask is set + * @see Float16Vector#toIntArray() + */ + @ForceInline + public final + void intoArray(short[] a, int offset, + int[] indexMap, int mapOffset, + VectorMask m) { + stOp(a, offset, m, + (arr, off, i, e) -> { + int j = indexMap[mapOffset + i]; + arr[off + j] = e; + }); + } + + + + /** + * {@inheritDoc} + * @since 27 + */ + @Override + @ForceInline + public final + void intoMemorySegment(MemorySegment ms, long offset, + ByteOrder bo) { + if (ms.isReadOnly()) { + throw new UnsupportedOperationException("Attempt to write a read-only segment"); + } + + offset = checkFromIndexSize(offset, byteSize(), ms.byteSize()); + maybeSwap(bo).intoMemorySegment0(ms, offset); + } + + /** + * {@inheritDoc} + * @since 27 + */ + @Override + @ForceInline + public final + void intoMemorySegment(MemorySegment ms, long offset, + ByteOrder bo, + VectorMask m) { + if (m.allTrue()) { + intoMemorySegment(ms, offset, bo); + } else { + if (ms.isReadOnly()) { + throw new UnsupportedOperationException("Attempt to write a read-only segment"); + } + Float16Species vsp = vspecies(); + if (!VectorIntrinsics.indexInRange(offset, vsp.vectorByteSize(), ms.byteSize())) { + ((AbstractMask)m) + .checkIndexByLane(offset, ms.byteSize(), vsp.iota(), 2); + } + maybeSwap(bo).intoMemorySegment0(ms, offset, m); + } + } + + // ================================================ + + // Low-level memory operations. + // + // Note that all of these operations *must* inline into a context + // where the exact species of the involved vector is a + // compile-time constant. Otherwise, the intrinsic generation + // will fail and performance will suffer. + // + // In many cases this is achieved by re-deriving a version of the + // method in each concrete subclass (per species). The re-derived + // method simply calls one of these generic methods, with exact + // parameters for the controlling metadata, which is either a + // typed vector or constant species instance. + + // Unchecked loading operations in native byte order. + // Caller is responsible for applying index checks, masking, and + // byte swapping. + + /*package-private*/ + abstract + Float16Vector fromArray0(short[] a, int offset); + @ForceInline + final + Float16Vector fromArray0Template(short[] a, int offset) { + Float16Species vsp = vspecies(); + return VectorSupport.load( + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), + a, arrayAddress(a, offset), false, + a, offset, vsp, + (arr, off, s) -> s.ldOp(arr, (int) off, + (arr_, off_, i) -> arr_[off_ + i])); + } + + /*package-private*/ + abstract + Float16Vector fromArray0(short[] a, int offset, VectorMask m, int offsetInRange); + @ForceInline + final + > + Float16Vector fromArray0Template(Class maskClass, short[] a, int offset, M m, int offsetInRange) { + m.check(species()); + Float16Species vsp = vspecies(); + return VectorSupport.loadMasked( + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), + a, arrayAddress(a, offset), false, m, offsetInRange, + a, offset, vsp, + (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, + (arr_, off_, i) -> arr_[off_ + i])); + } + + /*package-private*/ + abstract + Float16Vector fromArray0(short[] a, int offset, + int[] indexMap, int mapOffset, + VectorMask m); + @ForceInline + final + > + Float16Vector fromArray0Template(Class maskClass, short[] a, int offset, + int[] indexMap, int mapOffset, M m) { + Float16Species vsp = vspecies(); + IntVector.IntSpecies isp = IntVector.species(vsp.indexShape()); + Objects.requireNonNull(a); + Objects.requireNonNull(indexMap); + m.check(vsp); + Class vectorType = vsp.vectorType(); + + + // Constant folding should sweep out following conditonal logic. + VectorSpecies lsp; + if (isp.length() > IntVector.SPECIES_PREFERRED.length()) { + lsp = IntVector.SPECIES_PREFERRED; + } else { + lsp = isp; + } + + // Check indices are within array bounds. + // FIXME: Check index under mask controlling. + IntVector vix0 = IntVector.fromArray(lsp, indexMap, mapOffset).add(offset); + VectorIntrinsics.checkIndex(vix0, a.length); + + int vlen = vsp.length(); + int idx_vlen = lsp.length(); + IntVector vix1 = null; + if (vlen >= idx_vlen * 2) { + vix1 = IntVector.fromArray(lsp, indexMap, mapOffset + idx_vlen).add(offset); + VectorIntrinsics.checkIndex(vix1, a.length); + } + + return VectorSupport.loadWithMap( + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), + lsp.vectorType(), lsp.length(), + a, ARRAY_BASE, vix0, vix1, null, null, m, + a, offset, indexMap, mapOffset, vsp, + (c, idx, iMap, idy, s, vm) -> + s.vOp(vm, n -> c[idx + iMap[idy+n]])); + } + + + + abstract + Float16Vector fromMemorySegment0(MemorySegment bb, long offset); + @ForceInline + final + Float16Vector fromMemorySegment0Template(MemorySegment ms, long offset) { + Float16Species vsp = vspecies(); + return ScopedMemoryAccess.loadFromMemorySegment( + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), + (AbstractMemorySegmentImpl) ms, offset, vsp, + (msp, off, s) -> { + return s.ldLongOp((MemorySegment) msp, off, Float16Vector::memorySegmentGet); + }); + } + + abstract + Float16Vector fromMemorySegment0(MemorySegment ms, long offset, VectorMask m, int offsetInRange); + @ForceInline + final + > + Float16Vector fromMemorySegment0Template(Class maskClass, MemorySegment ms, long offset, M m, int offsetInRange) { + Float16Species vsp = vspecies(); + m.check(vsp); + return ScopedMemoryAccess.loadFromMemorySegmentMasked( + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), + (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, + (msp, off, s, vm) -> { + return s.ldLongOp((MemorySegment) msp, off, vm, Float16Vector::memorySegmentGet); + }); + } + + // Unchecked storing operations in native byte order. + // Caller is responsible for applying index checks, masking, and + // byte swapping. + + abstract + void intoArray0(short[] a, int offset); + @ForceInline + final + void intoArray0Template(short[] a, int offset) { + Float16Species vsp = vspecies(); + VectorSupport.store( + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), + a, arrayAddress(a, offset), false, + this, a, offset, + (arr, off, v) + -> v.stOp(arr, (int) off, + (arr_, off_, i, e) -> arr_[off_+i] = e)); + } + + abstract + void intoArray0(short[] a, int offset, VectorMask m); + @ForceInline + final + > + void intoArray0Template(Class maskClass, short[] a, int offset, M m) { + m.check(species()); + Float16Species vsp = vspecies(); + VectorSupport.storeMasked( + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), + a, arrayAddress(a, offset), false, + this, m, a, offset, + (arr, off, v, vm) + -> v.stOp(arr, (int) off, vm, + (arr_, off_, i, e) -> arr_[off_ + i] = e)); + } + + + + @ForceInline + final + void intoMemorySegment0(MemorySegment ms, long offset) { + Float16Species vsp = vspecies(); + ScopedMemoryAccess.storeIntoMemorySegment( + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), + this, + (AbstractMemorySegmentImpl) ms, offset, + (msp, off, v) -> { + v.stLongOp((MemorySegment) msp, off, Float16Vector::memorySegmentSet); + }); + } + + abstract + void intoMemorySegment0(MemorySegment bb, long offset, VectorMask m); + @ForceInline + final + > + void intoMemorySegment0Template(Class maskClass, MemorySegment ms, long offset, M m) { + Float16Species vsp = vspecies(); + m.check(vsp); + ScopedMemoryAccess.storeIntoMemorySegmentMasked( + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), + this, m, + (AbstractMemorySegmentImpl) ms, offset, + (msp, off, v, vm) -> { + v.stLongOp((MemorySegment) msp, off, vm, Float16Vector::memorySegmentSet); + }); + } + + + // End of low-level memory operations. + + @ForceInline + private void conditionalStoreNYI(int offset, + Float16Species vsp, + VectorMask m, + int scale, + int limit) { + if (offset < 0 || offset + vsp.laneCount() * scale > limit) { + String msg = + String.format("unimplemented: store @%d in [0..%d), %s in %s", + offset, limit, m, vsp); + throw new AssertionError(msg); + } + } + + /*package-private*/ + @Override + @ForceInline + final + Float16Vector maybeSwap(ByteOrder bo) { + if (bo != NATIVE_ENDIAN) { + return this.reinterpretAsBytes() + .rearrange(swapBytesShuffle()) + .reinterpretAsFloat16s(); + } + return this; + } + + @Override + @ForceInline + final + Float16Vector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (Float16Vector) this.rearrange(shuffle); + } + + static final int ARRAY_SHIFT = + 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_SHORT_INDEX_SCALE); + static final long ARRAY_BASE = + Unsafe.ARRAY_SHORT_BASE_OFFSET; + + @ForceInline + static long arrayAddress(short[] a, int index) { + return ARRAY_BASE + (((long)index) << ARRAY_SHIFT); + } + + + + @ForceInline + static long byteArrayAddress(byte[] a, int index) { + return Unsafe.ARRAY_BYTE_BASE_OFFSET + index; + } + + // ================================================ + + /// Reinterpreting view methods: + // lanewise reinterpret: viewAsXVector() + // keep shape, redraw lanes: reinterpretAsEs() + + /** + * {@inheritDoc} + */ + @ForceInline + @Override + public final ByteVector reinterpretAsBytes() { + // Going to ByteVector, pay close attention to byte order. + assert(REGISTER_ENDIAN == ByteOrder.LITTLE_ENDIAN); + return asByteVectorRaw(); + //return asByteVectorRaw().rearrange(swapBytesShuffle()); + } + + /** + * {@inheritDoc} + */ + @ForceInline + @Override + public final ShortVector viewAsIntegralLanes() { + LaneType ilt = LaneType.SHORT.asIntegral(); + return (ShortVector) asVectorRaw(ilt); + } + + /** + * {@inheritDoc} + */ + @ForceInline + @Override + public final + Float16Vector + viewAsFloatingLanes() { + return this; + } + + // ================================================ + + /// Object methods: toString, equals, hashCode + // + // Object methods are defined as if via Arrays.toString, etc., + // is applied to the array of elements. Two equal vectors + // are required to have equal species and equal lane values. + + /** + * Returns a string representation of this vector, of the form + * {@code "[0,1,2...]"}, reporting the lane values of this vector, + * in lane order. + * + * The string is produced as if by a call to {@link + * java.util.Arrays#toString(Object[]) Arrays.toString()}, + * as appropriate to a {@code Float16} array whose elements + * are obtained by applying {@link Float16#shortBitsToFloat16(short)} + * to each element of the {@code short[]} array returned by + * {@link #toArray this.toArray()}. + * + * @return a string of the form {@code "[0,1,2...]"} + * reporting the lane values of this vector + */ + @Override + @ForceInline + public final + String toString() { + // Render the lanes as Float16 values; Float16.toString produces + // human-readable text and canonicalizes NaN, Infinity and -0.0 + // independent of the underlying bit encoding. + return Arrays.toString(toFloat16Array()); + } + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + boolean equals(Object obj) { + if (obj instanceof Vector) { + Vector that = (Vector) obj; + if (this.species().equals(that.species())) { + return this.eq(that.check(this.species())).allTrue(); + } + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + @ForceInline + public final + int hashCode() { + // Hash the lanes as Float16 values; Float16.hashCode canonicalizes NaN + // so that all NaN representations contribute the same hash code. + return Objects.hash(species(), Arrays.hashCode(toFloat16Array())); + } + + // ================================================ + + // Species + + /** + * Class representing {@link Float16Vector}'s of the same {@link VectorShape VectorShape}. + */ + /*package-private*/ + static final class Float16Species extends AbstractSpecies { + private Float16Species(VectorShape shape, + Class vectorType, + Class> maskType, + Class> shuffleType, + Function vectorFactory) { + super(shape, LaneType.of(Float16.class), + vectorType, maskType, shuffleType, + vectorFactory); + assert(this.elementSize() == Float16.SIZE); + } + + // Specializing overrides: + + @Override + @ForceInline + public final Class elementType() { + return Float16.class; + } + + @Override + @ForceInline + final Class genericElementType() { + return Float16.class; + } + + @SuppressWarnings("unchecked") + @Override + @ForceInline + public final Class vectorType() { + return (Class) vectorType; + } + + @Override + @ForceInline + public final long checkValue(long e) { + longToElementBits(e); // only for exception + return e; + } + + /*package-private*/ + @Override + @ForceInline + final Float16Vector broadcastBits(long bits) { + return (Float16Vector) + VectorSupport.fromBitsCoerced( + vectorType, laneTypeOrdinal(), laneCount, + bits, MODE_BROADCAST, this, + (bits_, s_) -> s_.rvOp(i -> bits_)); + } + + /*package-private*/ + @ForceInline + final Float16Vector broadcast(short e) { + return broadcastBits(toBits(e)); + } + + @Override + @ForceInline + public final Float16Vector broadcast(long e) { + return broadcastBits(longToElementBits(e)); + } + + /*package-private*/ + final @Override + @ForceInline + long longToElementBits(long value) { + // Do the conversion, and then test it for failure. + short e = float16ToRawShortBits(Float16.valueOf(value)); + if (shortBitsToFloat16(e).longValue() != value) { + throw badElementBits(value, e); + } + return toBits(e); + } + + /*package-private*/ + @ForceInline + static long toIntegralChecked(short e, boolean convertToInt) { + float ef = shortBitsToFloat16(e).floatValue(); + long value = convertToInt ? (int) ef : (long) ef; + if ((float) value != ef) { + throw badArrayBits(e, convertToInt, value); + } + return value; + } + + /* this non-public one is for internal conversions */ + @Override + @ForceInline + final Float16Vector fromIntValues(int[] values) { + VectorIntrinsics.requireLength(values.length, laneCount); + short[] va = new short[laneCount()]; + for (int i = 0; i < va.length; i++) { + int lv = values[i]; + short v = float16ToRawShortBits(Float16.valueOf(lv)); + va[i] = v; + if (Float16.valueOf(lv).intValue() != lv) { + throw badElementBits(lv, v); + } + } + return dummyVector().fromArray0(va, 0); + } + + // Virtual constructors + + @ForceInline + @Override final + public Float16Vector fromArray(Object a, int offset) { + // User entry point + // Defer only to the equivalent method on the vector class, using the same inputs + return Float16Vector + .fromArray(this, (short[]) a, offset); + } + + @ForceInline + @Override final + public Float16Vector fromMemorySegment(MemorySegment ms, long offset, ByteOrder bo) { + // User entry point + // Defer only to the equivalent method on the vector class, using the same inputs + return Float16Vector + .fromMemorySegment(this, ms, offset, bo); + } + + @ForceInline + @Override final + Float16Vector dummyVector() { + return (Float16Vector) super.dummyVector(); + } + + /*package-private*/ + final @Override + @ForceInline + Float16Vector rvOp(RVOp f) { + short[] res = new short[laneCount()]; + for (int i = 0; i < res.length; i++) { + short bits = (short) f.apply(i); + res[i] = fromBits(bits); + } + return dummyVector().vectorFactory(res); + } + + Float16Vector vOp(FVOp f) { + short[] res = new short[laneCount()]; + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i); + } + return dummyVector().vectorFactory(res); + } + + Float16Vector vOp(VectorMask m, FVOp f) { + short[] res = new short[laneCount()]; + boolean[] mbits = ((AbstractMask)m).getBits(); + for (int i = 0; i < res.length; i++) { + if (mbits[i]) { + res[i] = f.apply(i); + } + } + return dummyVector().vectorFactory(res); + } + + /*package-private*/ + @ForceInline + Float16Vector ldOp(M memory, int offset, + FLdOp f) { + return dummyVector().ldOp(memory, offset, f); + } + + /*package-private*/ + @ForceInline + Float16Vector ldOp(M memory, int offset, + VectorMask m, + FLdOp f) { + return dummyVector().ldOp(memory, offset, m, f); + } + + /*package-private*/ + @ForceInline + Float16Vector ldLongOp(MemorySegment memory, long offset, + FLdLongOp f) { + return dummyVector().ldLongOp(memory, offset, f); + } + + /*package-private*/ + @ForceInline + Float16Vector ldLongOp(MemorySegment memory, long offset, + VectorMask m, + FLdLongOp f) { + return dummyVector().ldLongOp(memory, offset, m, f); + } + + /*package-private*/ + @ForceInline + void stOp(M memory, int offset, FStOp f) { + dummyVector().stOp(memory, offset, f); + } + + /*package-private*/ + @ForceInline + void stOp(M memory, int offset, + AbstractMask m, + FStOp f) { + dummyVector().stOp(memory, offset, m, f); + } + + /*package-private*/ + @ForceInline + void stLongOp(MemorySegment memory, long offset, FStLongOp f) { + dummyVector().stLongOp(memory, offset, f); + } + + /*package-private*/ + @ForceInline + void stLongOp(MemorySegment memory, long offset, + AbstractMask m, + FStLongOp f) { + dummyVector().stLongOp(memory, offset, m, f); + } + + // N.B. Make sure these constant vectors and + // masks load up correctly into registers. + // + // Also, see if we can avoid all that switching. + // Could we cache both vectors and both masks in + // this species object? + + // Zero and iota vector access + @Override + @ForceInline + public final Float16Vector zero() { + if ((Class) vectorType() == Float16VectorMax.class) + return Float16VectorMax.ZERO; + switch (vectorBitSize()) { + case 64: return Float16Vector64.ZERO; + case 128: return Float16Vector128.ZERO; + case 256: return Float16Vector256.ZERO; + case 512: return Float16Vector512.ZERO; + } + throw new AssertionError(); + } + + @Override + @ForceInline + public final Float16Vector iota() { + if ((Class) vectorType() == Float16VectorMax.class) + return Float16VectorMax.IOTA; + switch (vectorBitSize()) { + case 64: return Float16Vector64.IOTA; + case 128: return Float16Vector128.IOTA; + case 256: return Float16Vector256.IOTA; + case 512: return Float16Vector512.IOTA; + } + throw new AssertionError(); + } + + // Mask access + @Override + @ForceInline + public final VectorMask maskAll(boolean bit) { + if ((Class) vectorType() == Float16VectorMax.class) + return Float16VectorMax.Float16MaskMax.maskAll(bit); + switch (vectorBitSize()) { + case 64: return Float16Vector64.Float16Mask64.maskAll(bit); + case 128: return Float16Vector128.Float16Mask128.maskAll(bit); + case 256: return Float16Vector256.Float16Mask256.maskAll(bit); + case 512: return Float16Vector512.Float16Mask512.maskAll(bit); + } + throw new AssertionError(); + } + } + + /** + * Finds a species for an element type of {@code Float16} and shape. + * + * @param s the shape + * @return a species for an element type of {@code Float16} and shape + * @throws IllegalArgumentException if no such species exists for the shape + */ + static Float16Species species(VectorShape s) { + Objects.requireNonNull(s); + switch (s.switchKey) { + case VectorShape.SK_64_BIT: return (Float16Species) SPECIES_64; + case VectorShape.SK_128_BIT: return (Float16Species) SPECIES_128; + case VectorShape.SK_256_BIT: return (Float16Species) SPECIES_256; + case VectorShape.SK_512_BIT: return (Float16Species) SPECIES_512; + case VectorShape.SK_Max_BIT: return (Float16Species) SPECIES_MAX; + default: throw new IllegalArgumentException("Bad shape: " + s); + } + } + + /** Species representing {@link Float16Vector}s of {@link VectorShape#S_64_BIT VectorShape.S_64_BIT}. */ + public static final VectorSpecies SPECIES_64 + = new Float16Species(VectorShape.S_64_BIT, + Float16Vector64.class, + Float16Vector64.Float16Mask64.class, + Float16Vector64.Float16Shuffle64.class, + Float16Vector64::new); + + /** Species representing {@link Float16Vector}s of {@link VectorShape#S_128_BIT VectorShape.S_128_BIT}. */ + public static final VectorSpecies SPECIES_128 + = new Float16Species(VectorShape.S_128_BIT, + Float16Vector128.class, + Float16Vector128.Float16Mask128.class, + Float16Vector128.Float16Shuffle128.class, + Float16Vector128::new); + + /** Species representing {@link Float16Vector}s of {@link VectorShape#S_256_BIT VectorShape.S_256_BIT}. */ + public static final VectorSpecies SPECIES_256 + = new Float16Species(VectorShape.S_256_BIT, + Float16Vector256.class, + Float16Vector256.Float16Mask256.class, + Float16Vector256.Float16Shuffle256.class, + Float16Vector256::new); + + /** Species representing {@link Float16Vector}s of {@link VectorShape#S_512_BIT VectorShape.S_512_BIT}. */ + public static final VectorSpecies SPECIES_512 + = new Float16Species(VectorShape.S_512_BIT, + Float16Vector512.class, + Float16Vector512.Float16Mask512.class, + Float16Vector512.Float16Shuffle512.class, + Float16Vector512::new); + + /** Species representing {@link Float16Vector}s of {@link VectorShape#S_Max_BIT VectorShape.S_Max_BIT}. */ + public static final VectorSpecies SPECIES_MAX + = new Float16Species(VectorShape.S_Max_BIT, + Float16VectorMax.class, + Float16VectorMax.Float16MaskMax.class, + Float16VectorMax.Float16ShuffleMax.class, + Float16VectorMax::new); + + /** + * Preferred species for {@link Float16Vector}s. + * A preferred species is a species of maximal bit-size for the platform. + */ + public static final VectorSpecies SPECIES_PREFERRED + = (Float16Species) VectorSpecies.ofPreferred(Float16.class); +} + diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector128.java new file mode 100644 index 00000000000..65e2a2e1ba0 --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector128.java @@ -0,0 +1,1037 @@ +/* + * 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 + * 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 jdk.incubator.vector; + +import java.lang.foreign.MemorySegment; +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.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 Float16Vector128 extends Float16Vector { + static final Float16Species VSPECIES = + (Float16Species) Float16Vector.SPECIES_128; + + static final VectorShape VSHAPE = + VSPECIES.vectorShape(); + + static final Class VCLASS = Float16Vector128.class; + + static final int VSIZE = VSPECIES.vectorBitSize(); + + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // carrier type used by the JVM + + static final Class ETYPE = Float16.class; // used by the JVM + + Float16Vector128(short[] v) { + super(v); + } + + // For compatibility as Float16Vector128::new, + // stored into species.vectorFactory. + Float16Vector128(Object v) { + this((short[]) v); + } + + static final Float16Vector128 ZERO = new Float16Vector128(new short[VLENGTH]); + static final Float16Vector128 IOTA = new Float16Vector128(VSPECIES.iotaArray()); + + static { + // Warm up a few species caches. + // If we do this too much we will + // get NPEs from bootstrap circularity. + VSPECIES.dummyVector(); + VSPECIES.withLanes(LaneType.BYTE); + } + + // Specialized extractors + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractVector, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + @Override + public final Class elementType() { return Float16.class; } + + @ForceInline + final Class carrierType() { return CTYPE; } + + @ForceInline + @Override + public final int elementSize() { return Float16.SIZE; } + + @ForceInline + @Override + public final VectorShape shape() { return VSHAPE; } + + @ForceInline + @Override + public final int length() { return VLENGTH; } + + @ForceInline + @Override + public final int bitSize() { return VSIZE; } + + @ForceInline + @Override + public final int byteSize() { return VSIZE / Byte.SIZE; } + + /*package-private*/ + @ForceInline + final @Override + short[] vec() { + return (short[])getPayload(); + } + + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + + // Virtualized constructors + + @Override + @ForceInline + public final Float16Vector128 broadcast(short e) { + return (Float16Vector128) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + public final Float16Vector128 broadcast(long e) { + return (Float16Vector128) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + Float16Mask128 maskFromArray(boolean[] bits) { + return new Float16Mask128(bits); + } + + @Override + @ForceInline + Float16Shuffle128 iotaShuffle() { return Float16Shuffle128.IOTA; } + + @Override + @ForceInline + Float16Shuffle128 iotaShuffle(int start, int step, boolean wrap) { + return (Float16Shuffle128) iotaShuffleTemplate((short) start, (short) step, wrap); + } + + @Override + @ForceInline + Float16Shuffle128 shuffleFromArray(int[] indices, int i) { return new Float16Shuffle128(indices, i); } + + @Override + @ForceInline + Float16Shuffle128 shuffleFromOp(IntUnaryOperator fn) { return new Float16Shuffle128(fn); } + + // Make a vector of the same species but the given elements: + @ForceInline + final @Override + Float16Vector128 vectorFactory(short[] vec) { + return new Float16Vector128(vec); + } + + @ForceInline + final @Override + ByteVector128 asByteVectorRaw() { + return (ByteVector128) super.asByteVectorRawTemplate(); // specialize + } + + @ForceInline + final @Override + AbstractVector asVectorRaw(LaneType laneType) { + return super.asVectorRawTemplate(laneType); // specialize + } + + // Unary operator + + @ForceInline + final @Override + Float16Vector128 uOp(FUnOp f) { + return (Float16Vector128) super.uOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector128 uOp(VectorMask m, FUnOp f) { + return (Float16Vector128) + super.uOpTemplate((Float16Mask128)m, f); // specialize + } + + @ForceInline + final @Override + Float16Vector128 uRawOp(FUnRawOp f) { + return (Float16Vector128) super.uRawOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector128 uRawOp(VectorMask m, FUnRawOp f) { + return (Float16Vector128) + super.uRawOpTemplate((Float16Mask128)m, f); // specialize + } + // Binary operator + + @ForceInline + final @Override + Float16Vector128 bOp(Vector v, FBinOp f) { + return (Float16Vector128) super.bOpTemplate((Float16Vector128)v, f); // specialize + } + + @ForceInline + final @Override + Float16Vector128 bOp(Vector v, + VectorMask m, FBinOp f) { + return (Float16Vector128) + super.bOpTemplate((Float16Vector128)v, (Float16Mask128)m, + f); // specialize + } + + // Ternary operator + + @ForceInline + final @Override + Float16Vector128 tOp(Vector v1, Vector v2, FTriOp f) { + return (Float16Vector128) + super.tOpTemplate((Float16Vector128)v1, (Float16Vector128)v2, + f); // specialize + } + + @ForceInline + final @Override + Float16Vector128 tOp(Vector v1, Vector v2, + VectorMask m, FTriOp f) { + return (Float16Vector128) + super.tOpTemplate((Float16Vector128)v1, (Float16Vector128)v2, + (Float16Mask128)m, f); // specialize + } + + @ForceInline + final @Override + short rOp(short v, VectorMask m, FBinOp f) { + return super.rOpTemplate(v, m, f); // specialize + } + + @Override + @ForceInline + public final + Vector convertShape(VectorOperators.Conversion conv, + VectorSpecies rsp, int part) { + return super.convertShapeTemplate(conv, rsp, part); // specialize + } + + @Override + @ForceInline + public final + Vector reinterpretShape(VectorSpecies toSpecies, int part) { + return super.reinterpretShapeTemplate(toSpecies, part); // specialize + } + + // Specialized algebraic operations: + + // The following definition forces a specialized version of this + // crucial method into the v-table of this class. A call to add() + // will inline to a call to lanewise(ADD,), at which point the JIT + // intrinsic will have the opcode of ADD, plus all the metadata + // for this particular class, enabling it to generate precise + // code. + // + // There is probably no benefit to the JIT to specialize the + // masked or broadcast versions of the lanewise method. + + @Override + @ForceInline + public Float16Vector128 lanewise(Unary op) { + return (Float16Vector128) super.lanewiseTemplate(op); // specialize + } + + @Override + @ForceInline + public Float16Vector128 lanewise(Unary op, VectorMask m) { + return (Float16Vector128) super.lanewiseTemplate(op, Float16Mask128.class, (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 lanewise(Binary op, Vector v) { + return (Float16Vector128) super.lanewiseTemplate(op, v); // specialize + } + + @Override + @ForceInline + public Float16Vector128 lanewise(Binary op, Vector v, VectorMask m) { + return (Float16Vector128) super.lanewiseTemplate(op, Float16Mask128.class, v, (Float16Mask128) m); // specialize + } + + + /*package-private*/ + @Override + @ForceInline + public final + Float16Vector128 + lanewise(Ternary op, Vector v1, Vector v2) { + return (Float16Vector128) super.lanewiseTemplate(op, v1, v2); // specialize + } + + @Override + @ForceInline + public final + Float16Vector128 + lanewise(Ternary op, Vector v1, Vector v2, VectorMask m) { + return (Float16Vector128) super.lanewiseTemplate(op, Float16Mask128.class, v1, v2, (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public final + Float16Vector128 addIndex(int scale) { + return (Float16Vector128) super.addIndexTemplate(scale); // specialize + } + + // Type specific horizontal reductions + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op) { + return super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op, + VectorMask m) { + return super.reduceLanesTemplate(op, Float16Mask128.class, (Float16Mask128) m); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op) { + return (long) super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op, + VectorMask m) { + return (long) super.reduceLanesTemplate(op, Float16Mask128.class, (Float16Mask128) m); // specialized + } + + @Override + @ForceInline + final VectorShuffle bitsToShuffle(AbstractSpecies dsp) { + throw new AssertionError(); + } + + @Override + @ForceInline + public final Float16Shuffle128 toShuffle() { + return (Float16Shuffle128) toShuffle(VSPECIES, false); + } + + // Specialized unary testing + + @Override + @ForceInline + public final Float16Mask128 test(Test op) { + return super.testTemplate(Float16Mask128.class, op); // specialize + } + + @Override + @ForceInline + public final Float16Mask128 test(Test op, VectorMask m) { + return super.testTemplate(Float16Mask128.class, op, (Float16Mask128) m); // specialize + } + + // Specialized comparisons + + @Override + @ForceInline + public final Float16Mask128 compare(Comparison op, Vector v) { + return super.compareTemplate(Float16Mask128.class, op, v); // specialize + } + + @Override + @ForceInline + public final Float16Mask128 compare(Comparison op, short s) { + return super.compareTemplate(Float16Mask128.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask128 compare(Comparison op, long s) { + return super.compareTemplate(Float16Mask128.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask128 compare(Comparison op, Vector v, VectorMask m) { + return super.compareTemplate(Float16Mask128.class, op, v, (Float16Mask128) m); + } + + + @Override + @ForceInline + public Float16Vector128 blend(Vector v, VectorMask m) { + return (Float16Vector128) + super.blendTemplate(Float16Mask128.class, + (Float16Vector128) v, + (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 slice(int origin, Vector v) { + return (Float16Vector128) super.sliceTemplate(origin, v); // specialize + } + + @Override + @ForceInline + public Float16Vector128 slice(int origin) { + return (Float16Vector128) super.sliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector128 unslice(int origin, Vector w, int part) { + return (Float16Vector128) super.unsliceTemplate(origin, w, part); // specialize + } + + @Override + @ForceInline + public Float16Vector128 unslice(int origin, Vector w, int part, VectorMask m) { + return (Float16Vector128) + super.unsliceTemplate(Float16Mask128.class, + origin, w, part, + (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 unslice(int origin) { + return (Float16Vector128) super.unsliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector128 rearrange(VectorShuffle s) { + return (Float16Vector128) + super.rearrangeTemplate(Float16Shuffle128.class, + (Float16Shuffle128) s); // specialize + } + + @Override + @ForceInline + public Float16Vector128 rearrange(VectorShuffle shuffle, + VectorMask m) { + return (Float16Vector128) + super.rearrangeTemplate(Float16Shuffle128.class, + Float16Mask128.class, + (Float16Shuffle128) shuffle, + (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 rearrange(VectorShuffle s, + Vector v) { + return (Float16Vector128) + super.rearrangeTemplate(Float16Shuffle128.class, + (Float16Shuffle128) s, + (Float16Vector128) v); // specialize + } + + @Override + @ForceInline + public Float16Vector128 compress(VectorMask m) { + return (Float16Vector128) + super.compressTemplate(Float16Mask128.class, + (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 expand(VectorMask m) { + return (Float16Vector128) + super.expandTemplate(Float16Mask128.class, + (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 selectFrom(Vector v) { + return (Float16Vector128) + super.selectFromTemplate((Float16Vector128) v); // specialize + } + + @Override + @ForceInline + public Float16Vector128 selectFrom(Vector v, + VectorMask m) { + return (Float16Vector128) + super.selectFromTemplate((Float16Vector128) v, + Float16Mask128.class, (Float16Mask128) m); // specialize + } + + @Override + @ForceInline + public Float16Vector128 selectFrom(Vector v1, + Vector v2) { + return (Float16Vector128) + super.selectFromTemplate((Float16Vector128) v1, (Float16Vector128) v2); // specialize + } + + @ForceInline + @Override + public short lane(int i) { + short bits; + switch(i) { + case 0: bits = laneHelper(0); break; + case 1: bits = laneHelper(1); break; + case 2: bits = laneHelper(2); break; + case 3: bits = laneHelper(3); break; + case 4: bits = laneHelper(4); break; + case 5: bits = laneHelper(5); break; + case 6: bits = laneHelper(6); break; + case 7: bits = laneHelper(7); break; + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + return bits; + } + + @ForceInline + public short laneHelper(int i) { + return (short) VectorSupport.extract( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, + (vec, ix) -> { + short[] vecarr = vec.vec(); + return vecarr[ix]; + }); + } + + @ForceInline + @Override + public Float16Vector128 withLane(int i, short e) { + switch(i) { + case 0: return withLaneHelper(0, e); + case 1: return withLaneHelper(1, e); + case 2: return withLaneHelper(2, e); + case 3: return withLaneHelper(3, e); + case 4: return withLaneHelper(4, e); + case 5: return withLaneHelper(5, e); + case 6: return withLaneHelper(6, e); + case 7: return withLaneHelper(7, e); + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + } + + @ForceInline + public Float16Vector128 withLaneHelper(int i, short e) { + return VectorSupport.insert( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (long)e, + (v, ix, bits) -> { + short[] res = v.vec().clone(); + res[ix] = (short)bits; + return v.vectorFactory(res); + }); + } + + // Mask + @ValueBased + static final class Float16Mask128 extends AbstractMask { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Mask128(boolean[] bits) { + this(bits, 0); + } + + Float16Mask128(boolean[] bits, int offset) { + super(prepare(bits, offset)); + } + + Float16Mask128(boolean val) { + super(prepare(val)); + } + + private static boolean[] prepare(boolean[] bits, int offset) { + boolean[] newBits = new boolean[VSPECIES.laneCount()]; + for (int i = 0; i < newBits.length; i++) { + newBits[i] = bits[offset + i]; + } + return newBits; + } + + private static boolean[] prepare(boolean val) { + boolean[] bits = new boolean[VSPECIES.laneCount()]; + Arrays.fill(bits, val); + return bits; + } + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractMask, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + boolean[] getBits() { + return (boolean[])getPayload(); + } + + @Override + Float16Mask128 uOp(MUnOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i]); + } + return new Float16Mask128(res); + } + + @Override + Float16Mask128 bOp(VectorMask m, MBinOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + boolean[] mbits = ((Float16Mask128)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i], mbits[i]); + } + return new Float16Mask128(res); + } + + @ForceInline + @Override + public final + Float16Vector128 toVector() { + return (Float16Vector128) super.toVectorTemplate(); // specialize + } + + /** + * Helper function for lane-wise mask conversions. + * This function kicks in after intrinsic failure. + */ + @ForceInline + private final + VectorMask defaultMaskCast(AbstractSpecies dsp) { + if (length() != dsp.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + boolean[] maskArray = toArray(); + return dsp.maskFactory(maskArray).check(dsp); + } + + @Override + @ForceInline + public VectorMask cast(VectorSpecies dsp) { + AbstractSpecies species = (AbstractSpecies) dsp; + if (length() != species.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + + return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, + this, species, + (m, s) -> s.maskFactory(m.toArray()).check(s)); + } + + @Override + @ForceInline + /*package-private*/ + Float16Mask128 indexPartiallyInUpperRange(long offset, long limit) { + return (Float16Mask128) VectorSupport.indexPartiallyInUpperRange( + Float16Mask128.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, + (o, l) -> (Float16Mask128) TRUE_MASK.indexPartiallyInRange(o, l)); + } + + // Unary operations + + @Override + @ForceInline + public Float16Mask128 not() { + return xor(maskAll(true)); + } + + @Override + @ForceInline + public Float16Mask128 compress() { + return (Float16Mask128)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, + Float16Vector128.class, Float16Mask128.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, + (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, Float16.float16ToRawShortBits(Float16.valueOf(m1.trueCount())))); + } + + + // Binary operations + + @Override + @ForceInline + public Float16Mask128 and(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask128 m = (Float16Mask128)mask; + return VectorSupport.binaryOp(VECTOR_OP_AND, Float16Mask128.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); + } + + @Override + @ForceInline + public Float16Mask128 or(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask128 m = (Float16Mask128)mask; + return VectorSupport.binaryOp(VECTOR_OP_OR, Float16Mask128.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); + } + + @Override + @ForceInline + public Float16Mask128 xor(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask128 m = (Float16Mask128)mask; + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float16Mask128.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); + } + + // Mask Query operations + + @Override + @ForceInline + public int trueCount() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> trueCountHelper(m.getBits())); + } + + @Override + @ForceInline + public int firstTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> firstTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public int lastTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> lastTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public long toLong() { + if (length() > Long.SIZE) { + throw new UnsupportedOperationException("too many lanes for one long"); + } + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> toLongHelper(m.getBits())); + } + + // laneIsSet + + @Override + @ForceInline + public boolean laneIsSet(int i) { + Objects.checkIndex(i, length()); + return VectorSupport.extract(Float16Mask128.class, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; + } + + // Reductions + + @Override + @ForceInline + public boolean anyTrue() { + return VectorSupport.test(BT_ne, Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((Float16Mask128)m).getBits())); + } + + @Override + @ForceInline + public boolean allTrue() { + return VectorSupport.test(BT_overflow, Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((Float16Mask128)m).getBits())); + } + + @ForceInline + /*package-private*/ + static Float16Mask128 maskAll(boolean bit) { + return VectorSupport.fromBitsCoerced(Float16Mask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + (bit ? -1 : 0), MODE_BROADCAST, null, + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + } + private static final Float16Mask128 TRUE_MASK = new Float16Mask128(true); + private static final Float16Mask128 FALSE_MASK = new Float16Mask128(false); + + } + + // Shuffle + @ValueBased + static final class Float16Shuffle128 extends AbstractShuffle { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Shuffle128(short[] indices) { + super(indices); + assert(VLENGTH == indices.length); + assert(indicesInRange(indices)); + } + + Float16Shuffle128(int[] indices, int i) { + this(prepare(indices, i)); + } + + Float16Shuffle128(IntUnaryOperator fn) { + this(prepare(fn)); + } + + short[] indices() { + return (short[])getPayload(); + } + + @Override + @ForceInline + public Float16Species vspecies() { + return VSPECIES; + } + + static { + // There must be enough bits in the shuffle lanes to encode + // VLENGTH valid indexes and VLENGTH exceptional ones. + assert(VLENGTH < Short.MAX_VALUE); + assert(Short.MIN_VALUE <= -VLENGTH); + } + static final Float16Shuffle128 IOTA = new Float16Shuffle128(IDENTITY); + + @Override + @ForceInline + public Float16Vector128 toVector() { + return (Float16Vector128) toBitsVector().castShape(VSPECIES, 0); + } + + @Override + @ForceInline + ShortVector128 toBitsVector() { + return (ShortVector128) super.toBitsVectorTemplate(); + } + + @Override + ShortVector128 toBitsVector0() { + return ((ShortVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); + } + + @Override + @ForceInline + public int laneSource(int i) { + return (int)toBitsVector().lane(i); + } + + @Override + @ForceInline + public void intoArray(int[] a, int offset) { + VectorSpecies species = IntVector.SPECIES_128; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoArray(a, offset); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoArray(a, offset + species.length()); + } + + @Override + @ForceInline + public void intoMemorySegment(MemorySegment ms, long offset, ByteOrder bo) { + VectorSpecies species = IntVector.SPECIES_128; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoMemorySegment(ms, offset, bo); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoMemorySegment(ms, offset + species.vectorByteSize(), bo); + } + + @Override + @ForceInline + public final Float16Mask128 laneIsValid() { + return (Float16Mask128) toBitsVector().compare(VectorOperators.GE, 0) + .cast(VSPECIES); + } + + @ForceInline + @Override + public final Float16Shuffle128 rearrange(VectorShuffle shuffle) { + Float16Shuffle128 concreteShuffle = (Float16Shuffle128) shuffle; + return (Float16Shuffle128) toBitsVector().rearrange(concreteShuffle.cast(ShortVector.SPECIES_128)) + .toShuffle(VSPECIES, false); + } + + @ForceInline + @Override + public final Float16Shuffle128 wrapIndexes() { + ShortVector128 v = toBitsVector(); + if ((length() & (length() - 1)) == 0) { + v = (ShortVector128) v.lanewise(VectorOperators.AND, length() - 1); + } else { + v = (ShortVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), + v.compare(VectorOperators.LT, 0)); + } + return (Float16Shuffle128) v.toShuffle(VSPECIES, false); + } + + private static short[] prepare(int[] indices, int offset) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = indices[offset + i]; + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static short[] prepare(IntUnaryOperator f) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = f.applyAsInt(i); + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static boolean indicesInRange(short[] indices) { + int length = indices.length; + for (short si : indices) { + if (si >= (short)length || si < (short)(-length)) { + String msg = ("index "+si+"out of range ["+length+"] in "+ + java.util.Arrays.toString(indices)); + throw new AssertionError(msg); + } + } + return true; + } + } + + // ================================================ + + // Specialized low-level memory operations. + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset) { + return super.fromArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, VectorMask m, int offsetInRange) { + return super.fromArray0Template(Float16Mask128.class, a, offset, (Float16Mask128) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, int[] indexMap, int mapOffset, VectorMask m) { + return super.fromArray0Template(Float16Mask128.class, a, offset, indexMap, mapOffset, (Float16Mask128) m); + } + + + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset) { + return super.fromMemorySegment0Template(ms, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset, VectorMask m, int offsetInRange) { + return super.fromMemorySegment0Template(Float16Mask128.class, ms, offset, (Float16Mask128) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset) { + super.intoArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset, VectorMask m) { + super.intoArray0Template(Float16Mask128.class, a, offset, (Float16Mask128) m); + } + + + + @ForceInline + @Override + final + void intoMemorySegment0(MemorySegment ms, long offset, VectorMask m) { + super.intoMemorySegment0Template(Float16Mask128.class, ms, offset, (Float16Mask128) m); + } + + + // End of specialized low-level memory operations. + + // ================================================ + +} + diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector256.java new file mode 100644 index 00000000000..0ec038c5077 --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector256.java @@ -0,0 +1,1053 @@ +/* + * 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 + * 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 jdk.incubator.vector; + +import java.lang.foreign.MemorySegment; +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.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 Float16Vector256 extends Float16Vector { + static final Float16Species VSPECIES = + (Float16Species) Float16Vector.SPECIES_256; + + static final VectorShape VSHAPE = + VSPECIES.vectorShape(); + + static final Class VCLASS = Float16Vector256.class; + + static final int VSIZE = VSPECIES.vectorBitSize(); + + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // carrier type used by the JVM + + static final Class ETYPE = Float16.class; // used by the JVM + + Float16Vector256(short[] v) { + super(v); + } + + // For compatibility as Float16Vector256::new, + // stored into species.vectorFactory. + Float16Vector256(Object v) { + this((short[]) v); + } + + static final Float16Vector256 ZERO = new Float16Vector256(new short[VLENGTH]); + static final Float16Vector256 IOTA = new Float16Vector256(VSPECIES.iotaArray()); + + static { + // Warm up a few species caches. + // If we do this too much we will + // get NPEs from bootstrap circularity. + VSPECIES.dummyVector(); + VSPECIES.withLanes(LaneType.BYTE); + } + + // Specialized extractors + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractVector, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + @Override + public final Class elementType() { return Float16.class; } + + @ForceInline + final Class carrierType() { return CTYPE; } + + @ForceInline + @Override + public final int elementSize() { return Float16.SIZE; } + + @ForceInline + @Override + public final VectorShape shape() { return VSHAPE; } + + @ForceInline + @Override + public final int length() { return VLENGTH; } + + @ForceInline + @Override + public final int bitSize() { return VSIZE; } + + @ForceInline + @Override + public final int byteSize() { return VSIZE / Byte.SIZE; } + + /*package-private*/ + @ForceInline + final @Override + short[] vec() { + return (short[])getPayload(); + } + + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + + // Virtualized constructors + + @Override + @ForceInline + public final Float16Vector256 broadcast(short e) { + return (Float16Vector256) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + public final Float16Vector256 broadcast(long e) { + return (Float16Vector256) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + Float16Mask256 maskFromArray(boolean[] bits) { + return new Float16Mask256(bits); + } + + @Override + @ForceInline + Float16Shuffle256 iotaShuffle() { return Float16Shuffle256.IOTA; } + + @Override + @ForceInline + Float16Shuffle256 iotaShuffle(int start, int step, boolean wrap) { + return (Float16Shuffle256) iotaShuffleTemplate((short) start, (short) step, wrap); + } + + @Override + @ForceInline + Float16Shuffle256 shuffleFromArray(int[] indices, int i) { return new Float16Shuffle256(indices, i); } + + @Override + @ForceInline + Float16Shuffle256 shuffleFromOp(IntUnaryOperator fn) { return new Float16Shuffle256(fn); } + + // Make a vector of the same species but the given elements: + @ForceInline + final @Override + Float16Vector256 vectorFactory(short[] vec) { + return new Float16Vector256(vec); + } + + @ForceInline + final @Override + ByteVector256 asByteVectorRaw() { + return (ByteVector256) super.asByteVectorRawTemplate(); // specialize + } + + @ForceInline + final @Override + AbstractVector asVectorRaw(LaneType laneType) { + return super.asVectorRawTemplate(laneType); // specialize + } + + // Unary operator + + @ForceInline + final @Override + Float16Vector256 uOp(FUnOp f) { + return (Float16Vector256) super.uOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector256 uOp(VectorMask m, FUnOp f) { + return (Float16Vector256) + super.uOpTemplate((Float16Mask256)m, f); // specialize + } + + @ForceInline + final @Override + Float16Vector256 uRawOp(FUnRawOp f) { + return (Float16Vector256) super.uRawOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector256 uRawOp(VectorMask m, FUnRawOp f) { + return (Float16Vector256) + super.uRawOpTemplate((Float16Mask256)m, f); // specialize + } + // Binary operator + + @ForceInline + final @Override + Float16Vector256 bOp(Vector v, FBinOp f) { + return (Float16Vector256) super.bOpTemplate((Float16Vector256)v, f); // specialize + } + + @ForceInline + final @Override + Float16Vector256 bOp(Vector v, + VectorMask m, FBinOp f) { + return (Float16Vector256) + super.bOpTemplate((Float16Vector256)v, (Float16Mask256)m, + f); // specialize + } + + // Ternary operator + + @ForceInline + final @Override + Float16Vector256 tOp(Vector v1, Vector v2, FTriOp f) { + return (Float16Vector256) + super.tOpTemplate((Float16Vector256)v1, (Float16Vector256)v2, + f); // specialize + } + + @ForceInline + final @Override + Float16Vector256 tOp(Vector v1, Vector v2, + VectorMask m, FTriOp f) { + return (Float16Vector256) + super.tOpTemplate((Float16Vector256)v1, (Float16Vector256)v2, + (Float16Mask256)m, f); // specialize + } + + @ForceInline + final @Override + short rOp(short v, VectorMask m, FBinOp f) { + return super.rOpTemplate(v, m, f); // specialize + } + + @Override + @ForceInline + public final + Vector convertShape(VectorOperators.Conversion conv, + VectorSpecies rsp, int part) { + return super.convertShapeTemplate(conv, rsp, part); // specialize + } + + @Override + @ForceInline + public final + Vector reinterpretShape(VectorSpecies toSpecies, int part) { + return super.reinterpretShapeTemplate(toSpecies, part); // specialize + } + + // Specialized algebraic operations: + + // The following definition forces a specialized version of this + // crucial method into the v-table of this class. A call to add() + // will inline to a call to lanewise(ADD,), at which point the JIT + // intrinsic will have the opcode of ADD, plus all the metadata + // for this particular class, enabling it to generate precise + // code. + // + // There is probably no benefit to the JIT to specialize the + // masked or broadcast versions of the lanewise method. + + @Override + @ForceInline + public Float16Vector256 lanewise(Unary op) { + return (Float16Vector256) super.lanewiseTemplate(op); // specialize + } + + @Override + @ForceInline + public Float16Vector256 lanewise(Unary op, VectorMask m) { + return (Float16Vector256) super.lanewiseTemplate(op, Float16Mask256.class, (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 lanewise(Binary op, Vector v) { + return (Float16Vector256) super.lanewiseTemplate(op, v); // specialize + } + + @Override + @ForceInline + public Float16Vector256 lanewise(Binary op, Vector v, VectorMask m) { + return (Float16Vector256) super.lanewiseTemplate(op, Float16Mask256.class, v, (Float16Mask256) m); // specialize + } + + + /*package-private*/ + @Override + @ForceInline + public final + Float16Vector256 + lanewise(Ternary op, Vector v1, Vector v2) { + return (Float16Vector256) super.lanewiseTemplate(op, v1, v2); // specialize + } + + @Override + @ForceInline + public final + Float16Vector256 + lanewise(Ternary op, Vector v1, Vector v2, VectorMask m) { + return (Float16Vector256) super.lanewiseTemplate(op, Float16Mask256.class, v1, v2, (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public final + Float16Vector256 addIndex(int scale) { + return (Float16Vector256) super.addIndexTemplate(scale); // specialize + } + + // Type specific horizontal reductions + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op) { + return super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op, + VectorMask m) { + return super.reduceLanesTemplate(op, Float16Mask256.class, (Float16Mask256) m); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op) { + return (long) super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op, + VectorMask m) { + return (long) super.reduceLanesTemplate(op, Float16Mask256.class, (Float16Mask256) m); // specialized + } + + @Override + @ForceInline + final VectorShuffle bitsToShuffle(AbstractSpecies dsp) { + throw new AssertionError(); + } + + @Override + @ForceInline + public final Float16Shuffle256 toShuffle() { + return (Float16Shuffle256) toShuffle(VSPECIES, false); + } + + // Specialized unary testing + + @Override + @ForceInline + public final Float16Mask256 test(Test op) { + return super.testTemplate(Float16Mask256.class, op); // specialize + } + + @Override + @ForceInline + public final Float16Mask256 test(Test op, VectorMask m) { + return super.testTemplate(Float16Mask256.class, op, (Float16Mask256) m); // specialize + } + + // Specialized comparisons + + @Override + @ForceInline + public final Float16Mask256 compare(Comparison op, Vector v) { + return super.compareTemplate(Float16Mask256.class, op, v); // specialize + } + + @Override + @ForceInline + public final Float16Mask256 compare(Comparison op, short s) { + return super.compareTemplate(Float16Mask256.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask256 compare(Comparison op, long s) { + return super.compareTemplate(Float16Mask256.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask256 compare(Comparison op, Vector v, VectorMask m) { + return super.compareTemplate(Float16Mask256.class, op, v, (Float16Mask256) m); + } + + + @Override + @ForceInline + public Float16Vector256 blend(Vector v, VectorMask m) { + return (Float16Vector256) + super.blendTemplate(Float16Mask256.class, + (Float16Vector256) v, + (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 slice(int origin, Vector v) { + return (Float16Vector256) super.sliceTemplate(origin, v); // specialize + } + + @Override + @ForceInline + public Float16Vector256 slice(int origin) { + return (Float16Vector256) super.sliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector256 unslice(int origin, Vector w, int part) { + return (Float16Vector256) super.unsliceTemplate(origin, w, part); // specialize + } + + @Override + @ForceInline + public Float16Vector256 unslice(int origin, Vector w, int part, VectorMask m) { + return (Float16Vector256) + super.unsliceTemplate(Float16Mask256.class, + origin, w, part, + (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 unslice(int origin) { + return (Float16Vector256) super.unsliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector256 rearrange(VectorShuffle s) { + return (Float16Vector256) + super.rearrangeTemplate(Float16Shuffle256.class, + (Float16Shuffle256) s); // specialize + } + + @Override + @ForceInline + public Float16Vector256 rearrange(VectorShuffle shuffle, + VectorMask m) { + return (Float16Vector256) + super.rearrangeTemplate(Float16Shuffle256.class, + Float16Mask256.class, + (Float16Shuffle256) shuffle, + (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 rearrange(VectorShuffle s, + Vector v) { + return (Float16Vector256) + super.rearrangeTemplate(Float16Shuffle256.class, + (Float16Shuffle256) s, + (Float16Vector256) v); // specialize + } + + @Override + @ForceInline + public Float16Vector256 compress(VectorMask m) { + return (Float16Vector256) + super.compressTemplate(Float16Mask256.class, + (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 expand(VectorMask m) { + return (Float16Vector256) + super.expandTemplate(Float16Mask256.class, + (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 selectFrom(Vector v) { + return (Float16Vector256) + super.selectFromTemplate((Float16Vector256) v); // specialize + } + + @Override + @ForceInline + public Float16Vector256 selectFrom(Vector v, + VectorMask m) { + return (Float16Vector256) + super.selectFromTemplate((Float16Vector256) v, + Float16Mask256.class, (Float16Mask256) m); // specialize + } + + @Override + @ForceInline + public Float16Vector256 selectFrom(Vector v1, + Vector v2) { + return (Float16Vector256) + super.selectFromTemplate((Float16Vector256) v1, (Float16Vector256) v2); // specialize + } + + @ForceInline + @Override + public short lane(int i) { + short bits; + switch(i) { + case 0: bits = laneHelper(0); break; + case 1: bits = laneHelper(1); break; + case 2: bits = laneHelper(2); break; + case 3: bits = laneHelper(3); break; + case 4: bits = laneHelper(4); break; + case 5: bits = laneHelper(5); break; + case 6: bits = laneHelper(6); break; + case 7: bits = laneHelper(7); break; + case 8: bits = laneHelper(8); break; + case 9: bits = laneHelper(9); break; + case 10: bits = laneHelper(10); break; + case 11: bits = laneHelper(11); break; + case 12: bits = laneHelper(12); break; + case 13: bits = laneHelper(13); break; + case 14: bits = laneHelper(14); break; + case 15: bits = laneHelper(15); break; + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + return bits; + } + + @ForceInline + public short laneHelper(int i) { + return (short) VectorSupport.extract( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, + (vec, ix) -> { + short[] vecarr = vec.vec(); + return vecarr[ix]; + }); + } + + @ForceInline + @Override + public Float16Vector256 withLane(int i, short e) { + switch(i) { + case 0: return withLaneHelper(0, e); + case 1: return withLaneHelper(1, e); + case 2: return withLaneHelper(2, e); + case 3: return withLaneHelper(3, e); + case 4: return withLaneHelper(4, e); + case 5: return withLaneHelper(5, e); + case 6: return withLaneHelper(6, e); + case 7: return withLaneHelper(7, e); + case 8: return withLaneHelper(8, e); + case 9: return withLaneHelper(9, e); + case 10: return withLaneHelper(10, e); + case 11: return withLaneHelper(11, e); + case 12: return withLaneHelper(12, e); + case 13: return withLaneHelper(13, e); + case 14: return withLaneHelper(14, e); + case 15: return withLaneHelper(15, e); + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + } + + @ForceInline + public Float16Vector256 withLaneHelper(int i, short e) { + return VectorSupport.insert( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (long)e, + (v, ix, bits) -> { + short[] res = v.vec().clone(); + res[ix] = (short)bits; + return v.vectorFactory(res); + }); + } + + // Mask + @ValueBased + static final class Float16Mask256 extends AbstractMask { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Mask256(boolean[] bits) { + this(bits, 0); + } + + Float16Mask256(boolean[] bits, int offset) { + super(prepare(bits, offset)); + } + + Float16Mask256(boolean val) { + super(prepare(val)); + } + + private static boolean[] prepare(boolean[] bits, int offset) { + boolean[] newBits = new boolean[VSPECIES.laneCount()]; + for (int i = 0; i < newBits.length; i++) { + newBits[i] = bits[offset + i]; + } + return newBits; + } + + private static boolean[] prepare(boolean val) { + boolean[] bits = new boolean[VSPECIES.laneCount()]; + Arrays.fill(bits, val); + return bits; + } + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractMask, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + boolean[] getBits() { + return (boolean[])getPayload(); + } + + @Override + Float16Mask256 uOp(MUnOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i]); + } + return new Float16Mask256(res); + } + + @Override + Float16Mask256 bOp(VectorMask m, MBinOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + boolean[] mbits = ((Float16Mask256)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i], mbits[i]); + } + return new Float16Mask256(res); + } + + @ForceInline + @Override + public final + Float16Vector256 toVector() { + return (Float16Vector256) super.toVectorTemplate(); // specialize + } + + /** + * Helper function for lane-wise mask conversions. + * This function kicks in after intrinsic failure. + */ + @ForceInline + private final + VectorMask defaultMaskCast(AbstractSpecies dsp) { + if (length() != dsp.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + boolean[] maskArray = toArray(); + return dsp.maskFactory(maskArray).check(dsp); + } + + @Override + @ForceInline + public VectorMask cast(VectorSpecies dsp) { + AbstractSpecies species = (AbstractSpecies) dsp; + if (length() != species.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + + return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, + this, species, + (m, s) -> s.maskFactory(m.toArray()).check(s)); + } + + @Override + @ForceInline + /*package-private*/ + Float16Mask256 indexPartiallyInUpperRange(long offset, long limit) { + return (Float16Mask256) VectorSupport.indexPartiallyInUpperRange( + Float16Mask256.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, + (o, l) -> (Float16Mask256) TRUE_MASK.indexPartiallyInRange(o, l)); + } + + // Unary operations + + @Override + @ForceInline + public Float16Mask256 not() { + return xor(maskAll(true)); + } + + @Override + @ForceInline + public Float16Mask256 compress() { + return (Float16Mask256)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, + Float16Vector256.class, Float16Mask256.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, + (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, Float16.float16ToRawShortBits(Float16.valueOf(m1.trueCount())))); + } + + + // Binary operations + + @Override + @ForceInline + public Float16Mask256 and(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask256 m = (Float16Mask256)mask; + return VectorSupport.binaryOp(VECTOR_OP_AND, Float16Mask256.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); + } + + @Override + @ForceInline + public Float16Mask256 or(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask256 m = (Float16Mask256)mask; + return VectorSupport.binaryOp(VECTOR_OP_OR, Float16Mask256.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); + } + + @Override + @ForceInline + public Float16Mask256 xor(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask256 m = (Float16Mask256)mask; + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float16Mask256.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); + } + + // Mask Query operations + + @Override + @ForceInline + public int trueCount() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> trueCountHelper(m.getBits())); + } + + @Override + @ForceInline + public int firstTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> firstTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public int lastTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> lastTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public long toLong() { + if (length() > Long.SIZE) { + throw new UnsupportedOperationException("too many lanes for one long"); + } + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> toLongHelper(m.getBits())); + } + + // laneIsSet + + @Override + @ForceInline + public boolean laneIsSet(int i) { + Objects.checkIndex(i, length()); + return VectorSupport.extract(Float16Mask256.class, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; + } + + // Reductions + + @Override + @ForceInline + public boolean anyTrue() { + return VectorSupport.test(BT_ne, Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((Float16Mask256)m).getBits())); + } + + @Override + @ForceInline + public boolean allTrue() { + return VectorSupport.test(BT_overflow, Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((Float16Mask256)m).getBits())); + } + + @ForceInline + /*package-private*/ + static Float16Mask256 maskAll(boolean bit) { + return VectorSupport.fromBitsCoerced(Float16Mask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + (bit ? -1 : 0), MODE_BROADCAST, null, + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + } + private static final Float16Mask256 TRUE_MASK = new Float16Mask256(true); + private static final Float16Mask256 FALSE_MASK = new Float16Mask256(false); + + } + + // Shuffle + @ValueBased + static final class Float16Shuffle256 extends AbstractShuffle { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Shuffle256(short[] indices) { + super(indices); + assert(VLENGTH == indices.length); + assert(indicesInRange(indices)); + } + + Float16Shuffle256(int[] indices, int i) { + this(prepare(indices, i)); + } + + Float16Shuffle256(IntUnaryOperator fn) { + this(prepare(fn)); + } + + short[] indices() { + return (short[])getPayload(); + } + + @Override + @ForceInline + public Float16Species vspecies() { + return VSPECIES; + } + + static { + // There must be enough bits in the shuffle lanes to encode + // VLENGTH valid indexes and VLENGTH exceptional ones. + assert(VLENGTH < Short.MAX_VALUE); + assert(Short.MIN_VALUE <= -VLENGTH); + } + static final Float16Shuffle256 IOTA = new Float16Shuffle256(IDENTITY); + + @Override + @ForceInline + public Float16Vector256 toVector() { + return (Float16Vector256) toBitsVector().castShape(VSPECIES, 0); + } + + @Override + @ForceInline + ShortVector256 toBitsVector() { + return (ShortVector256) super.toBitsVectorTemplate(); + } + + @Override + ShortVector256 toBitsVector0() { + return ((ShortVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); + } + + @Override + @ForceInline + public int laneSource(int i) { + return (int)toBitsVector().lane(i); + } + + @Override + @ForceInline + public void intoArray(int[] a, int offset) { + VectorSpecies species = IntVector.SPECIES_256; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoArray(a, offset); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoArray(a, offset + species.length()); + } + + @Override + @ForceInline + public void intoMemorySegment(MemorySegment ms, long offset, ByteOrder bo) { + VectorSpecies species = IntVector.SPECIES_256; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoMemorySegment(ms, offset, bo); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoMemorySegment(ms, offset + species.vectorByteSize(), bo); + } + + @Override + @ForceInline + public final Float16Mask256 laneIsValid() { + return (Float16Mask256) toBitsVector().compare(VectorOperators.GE, 0) + .cast(VSPECIES); + } + + @ForceInline + @Override + public final Float16Shuffle256 rearrange(VectorShuffle shuffle) { + Float16Shuffle256 concreteShuffle = (Float16Shuffle256) shuffle; + return (Float16Shuffle256) toBitsVector().rearrange(concreteShuffle.cast(ShortVector.SPECIES_256)) + .toShuffle(VSPECIES, false); + } + + @ForceInline + @Override + public final Float16Shuffle256 wrapIndexes() { + ShortVector256 v = toBitsVector(); + if ((length() & (length() - 1)) == 0) { + v = (ShortVector256) v.lanewise(VectorOperators.AND, length() - 1); + } else { + v = (ShortVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), + v.compare(VectorOperators.LT, 0)); + } + return (Float16Shuffle256) v.toShuffle(VSPECIES, false); + } + + private static short[] prepare(int[] indices, int offset) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = indices[offset + i]; + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static short[] prepare(IntUnaryOperator f) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = f.applyAsInt(i); + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static boolean indicesInRange(short[] indices) { + int length = indices.length; + for (short si : indices) { + if (si >= (short)length || si < (short)(-length)) { + String msg = ("index "+si+"out of range ["+length+"] in "+ + java.util.Arrays.toString(indices)); + throw new AssertionError(msg); + } + } + return true; + } + } + + // ================================================ + + // Specialized low-level memory operations. + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset) { + return super.fromArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, VectorMask m, int offsetInRange) { + return super.fromArray0Template(Float16Mask256.class, a, offset, (Float16Mask256) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, int[] indexMap, int mapOffset, VectorMask m) { + return super.fromArray0Template(Float16Mask256.class, a, offset, indexMap, mapOffset, (Float16Mask256) m); + } + + + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset) { + return super.fromMemorySegment0Template(ms, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset, VectorMask m, int offsetInRange) { + return super.fromMemorySegment0Template(Float16Mask256.class, ms, offset, (Float16Mask256) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset) { + super.intoArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset, VectorMask m) { + super.intoArray0Template(Float16Mask256.class, a, offset, (Float16Mask256) m); + } + + + + @ForceInline + @Override + final + void intoMemorySegment0(MemorySegment ms, long offset, VectorMask m) { + super.intoMemorySegment0Template(Float16Mask256.class, ms, offset, (Float16Mask256) m); + } + + + // End of specialized low-level memory operations. + + // ================================================ + +} + diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector512.java new file mode 100644 index 00000000000..9ec1eaad8ad --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector512.java @@ -0,0 +1,1085 @@ +/* + * 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 + * 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 jdk.incubator.vector; + +import java.lang.foreign.MemorySegment; +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.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 Float16Vector512 extends Float16Vector { + static final Float16Species VSPECIES = + (Float16Species) Float16Vector.SPECIES_512; + + static final VectorShape VSHAPE = + VSPECIES.vectorShape(); + + static final Class VCLASS = Float16Vector512.class; + + static final int VSIZE = VSPECIES.vectorBitSize(); + + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // carrier type used by the JVM + + static final Class ETYPE = Float16.class; // used by the JVM + + Float16Vector512(short[] v) { + super(v); + } + + // For compatibility as Float16Vector512::new, + // stored into species.vectorFactory. + Float16Vector512(Object v) { + this((short[]) v); + } + + static final Float16Vector512 ZERO = new Float16Vector512(new short[VLENGTH]); + static final Float16Vector512 IOTA = new Float16Vector512(VSPECIES.iotaArray()); + + static { + // Warm up a few species caches. + // If we do this too much we will + // get NPEs from bootstrap circularity. + VSPECIES.dummyVector(); + VSPECIES.withLanes(LaneType.BYTE); + } + + // Specialized extractors + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractVector, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + @Override + public final Class elementType() { return Float16.class; } + + @ForceInline + final Class carrierType() { return CTYPE; } + + @ForceInline + @Override + public final int elementSize() { return Float16.SIZE; } + + @ForceInline + @Override + public final VectorShape shape() { return VSHAPE; } + + @ForceInline + @Override + public final int length() { return VLENGTH; } + + @ForceInline + @Override + public final int bitSize() { return VSIZE; } + + @ForceInline + @Override + public final int byteSize() { return VSIZE / Byte.SIZE; } + + /*package-private*/ + @ForceInline + final @Override + short[] vec() { + return (short[])getPayload(); + } + + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + + // Virtualized constructors + + @Override + @ForceInline + public final Float16Vector512 broadcast(short e) { + return (Float16Vector512) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + public final Float16Vector512 broadcast(long e) { + return (Float16Vector512) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + Float16Mask512 maskFromArray(boolean[] bits) { + return new Float16Mask512(bits); + } + + @Override + @ForceInline + Float16Shuffle512 iotaShuffle() { return Float16Shuffle512.IOTA; } + + @Override + @ForceInline + Float16Shuffle512 iotaShuffle(int start, int step, boolean wrap) { + return (Float16Shuffle512) iotaShuffleTemplate((short) start, (short) step, wrap); + } + + @Override + @ForceInline + Float16Shuffle512 shuffleFromArray(int[] indices, int i) { return new Float16Shuffle512(indices, i); } + + @Override + @ForceInline + Float16Shuffle512 shuffleFromOp(IntUnaryOperator fn) { return new Float16Shuffle512(fn); } + + // Make a vector of the same species but the given elements: + @ForceInline + final @Override + Float16Vector512 vectorFactory(short[] vec) { + return new Float16Vector512(vec); + } + + @ForceInline + final @Override + ByteVector512 asByteVectorRaw() { + return (ByteVector512) super.asByteVectorRawTemplate(); // specialize + } + + @ForceInline + final @Override + AbstractVector asVectorRaw(LaneType laneType) { + return super.asVectorRawTemplate(laneType); // specialize + } + + // Unary operator + + @ForceInline + final @Override + Float16Vector512 uOp(FUnOp f) { + return (Float16Vector512) super.uOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector512 uOp(VectorMask m, FUnOp f) { + return (Float16Vector512) + super.uOpTemplate((Float16Mask512)m, f); // specialize + } + + @ForceInline + final @Override + Float16Vector512 uRawOp(FUnRawOp f) { + return (Float16Vector512) super.uRawOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector512 uRawOp(VectorMask m, FUnRawOp f) { + return (Float16Vector512) + super.uRawOpTemplate((Float16Mask512)m, f); // specialize + } + // Binary operator + + @ForceInline + final @Override + Float16Vector512 bOp(Vector v, FBinOp f) { + return (Float16Vector512) super.bOpTemplate((Float16Vector512)v, f); // specialize + } + + @ForceInline + final @Override + Float16Vector512 bOp(Vector v, + VectorMask m, FBinOp f) { + return (Float16Vector512) + super.bOpTemplate((Float16Vector512)v, (Float16Mask512)m, + f); // specialize + } + + // Ternary operator + + @ForceInline + final @Override + Float16Vector512 tOp(Vector v1, Vector v2, FTriOp f) { + return (Float16Vector512) + super.tOpTemplate((Float16Vector512)v1, (Float16Vector512)v2, + f); // specialize + } + + @ForceInline + final @Override + Float16Vector512 tOp(Vector v1, Vector v2, + VectorMask m, FTriOp f) { + return (Float16Vector512) + super.tOpTemplate((Float16Vector512)v1, (Float16Vector512)v2, + (Float16Mask512)m, f); // specialize + } + + @ForceInline + final @Override + short rOp(short v, VectorMask m, FBinOp f) { + return super.rOpTemplate(v, m, f); // specialize + } + + @Override + @ForceInline + public final + Vector convertShape(VectorOperators.Conversion conv, + VectorSpecies rsp, int part) { + return super.convertShapeTemplate(conv, rsp, part); // specialize + } + + @Override + @ForceInline + public final + Vector reinterpretShape(VectorSpecies toSpecies, int part) { + return super.reinterpretShapeTemplate(toSpecies, part); // specialize + } + + // Specialized algebraic operations: + + // The following definition forces a specialized version of this + // crucial method into the v-table of this class. A call to add() + // will inline to a call to lanewise(ADD,), at which point the JIT + // intrinsic will have the opcode of ADD, plus all the metadata + // for this particular class, enabling it to generate precise + // code. + // + // There is probably no benefit to the JIT to specialize the + // masked or broadcast versions of the lanewise method. + + @Override + @ForceInline + public Float16Vector512 lanewise(Unary op) { + return (Float16Vector512) super.lanewiseTemplate(op); // specialize + } + + @Override + @ForceInline + public Float16Vector512 lanewise(Unary op, VectorMask m) { + return (Float16Vector512) super.lanewiseTemplate(op, Float16Mask512.class, (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 lanewise(Binary op, Vector v) { + return (Float16Vector512) super.lanewiseTemplate(op, v); // specialize + } + + @Override + @ForceInline + public Float16Vector512 lanewise(Binary op, Vector v, VectorMask m) { + return (Float16Vector512) super.lanewiseTemplate(op, Float16Mask512.class, v, (Float16Mask512) m); // specialize + } + + + /*package-private*/ + @Override + @ForceInline + public final + Float16Vector512 + lanewise(Ternary op, Vector v1, Vector v2) { + return (Float16Vector512) super.lanewiseTemplate(op, v1, v2); // specialize + } + + @Override + @ForceInline + public final + Float16Vector512 + lanewise(Ternary op, Vector v1, Vector v2, VectorMask m) { + return (Float16Vector512) super.lanewiseTemplate(op, Float16Mask512.class, v1, v2, (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public final + Float16Vector512 addIndex(int scale) { + return (Float16Vector512) super.addIndexTemplate(scale); // specialize + } + + // Type specific horizontal reductions + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op) { + return super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op, + VectorMask m) { + return super.reduceLanesTemplate(op, Float16Mask512.class, (Float16Mask512) m); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op) { + return (long) super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op, + VectorMask m) { + return (long) super.reduceLanesTemplate(op, Float16Mask512.class, (Float16Mask512) m); // specialized + } + + @Override + @ForceInline + final VectorShuffle bitsToShuffle(AbstractSpecies dsp) { + throw new AssertionError(); + } + + @Override + @ForceInline + public final Float16Shuffle512 toShuffle() { + return (Float16Shuffle512) toShuffle(VSPECIES, false); + } + + // Specialized unary testing + + @Override + @ForceInline + public final Float16Mask512 test(Test op) { + return super.testTemplate(Float16Mask512.class, op); // specialize + } + + @Override + @ForceInline + public final Float16Mask512 test(Test op, VectorMask m) { + return super.testTemplate(Float16Mask512.class, op, (Float16Mask512) m); // specialize + } + + // Specialized comparisons + + @Override + @ForceInline + public final Float16Mask512 compare(Comparison op, Vector v) { + return super.compareTemplate(Float16Mask512.class, op, v); // specialize + } + + @Override + @ForceInline + public final Float16Mask512 compare(Comparison op, short s) { + return super.compareTemplate(Float16Mask512.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask512 compare(Comparison op, long s) { + return super.compareTemplate(Float16Mask512.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask512 compare(Comparison op, Vector v, VectorMask m) { + return super.compareTemplate(Float16Mask512.class, op, v, (Float16Mask512) m); + } + + + @Override + @ForceInline + public Float16Vector512 blend(Vector v, VectorMask m) { + return (Float16Vector512) + super.blendTemplate(Float16Mask512.class, + (Float16Vector512) v, + (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 slice(int origin, Vector v) { + return (Float16Vector512) super.sliceTemplate(origin, v); // specialize + } + + @Override + @ForceInline + public Float16Vector512 slice(int origin) { + return (Float16Vector512) super.sliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector512 unslice(int origin, Vector w, int part) { + return (Float16Vector512) super.unsliceTemplate(origin, w, part); // specialize + } + + @Override + @ForceInline + public Float16Vector512 unslice(int origin, Vector w, int part, VectorMask m) { + return (Float16Vector512) + super.unsliceTemplate(Float16Mask512.class, + origin, w, part, + (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 unslice(int origin) { + return (Float16Vector512) super.unsliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector512 rearrange(VectorShuffle s) { + return (Float16Vector512) + super.rearrangeTemplate(Float16Shuffle512.class, + (Float16Shuffle512) s); // specialize + } + + @Override + @ForceInline + public Float16Vector512 rearrange(VectorShuffle shuffle, + VectorMask m) { + return (Float16Vector512) + super.rearrangeTemplate(Float16Shuffle512.class, + Float16Mask512.class, + (Float16Shuffle512) shuffle, + (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 rearrange(VectorShuffle s, + Vector v) { + return (Float16Vector512) + super.rearrangeTemplate(Float16Shuffle512.class, + (Float16Shuffle512) s, + (Float16Vector512) v); // specialize + } + + @Override + @ForceInline + public Float16Vector512 compress(VectorMask m) { + return (Float16Vector512) + super.compressTemplate(Float16Mask512.class, + (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 expand(VectorMask m) { + return (Float16Vector512) + super.expandTemplate(Float16Mask512.class, + (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 selectFrom(Vector v) { + return (Float16Vector512) + super.selectFromTemplate((Float16Vector512) v); // specialize + } + + @Override + @ForceInline + public Float16Vector512 selectFrom(Vector v, + VectorMask m) { + return (Float16Vector512) + super.selectFromTemplate((Float16Vector512) v, + Float16Mask512.class, (Float16Mask512) m); // specialize + } + + @Override + @ForceInline + public Float16Vector512 selectFrom(Vector v1, + Vector v2) { + return (Float16Vector512) + super.selectFromTemplate((Float16Vector512) v1, (Float16Vector512) v2); // specialize + } + + @ForceInline + @Override + public short lane(int i) { + short bits; + switch(i) { + case 0: bits = laneHelper(0); break; + case 1: bits = laneHelper(1); break; + case 2: bits = laneHelper(2); break; + case 3: bits = laneHelper(3); break; + case 4: bits = laneHelper(4); break; + case 5: bits = laneHelper(5); break; + case 6: bits = laneHelper(6); break; + case 7: bits = laneHelper(7); break; + case 8: bits = laneHelper(8); break; + case 9: bits = laneHelper(9); break; + case 10: bits = laneHelper(10); break; + case 11: bits = laneHelper(11); break; + case 12: bits = laneHelper(12); break; + case 13: bits = laneHelper(13); break; + case 14: bits = laneHelper(14); break; + case 15: bits = laneHelper(15); break; + case 16: bits = laneHelper(16); break; + case 17: bits = laneHelper(17); break; + case 18: bits = laneHelper(18); break; + case 19: bits = laneHelper(19); break; + case 20: bits = laneHelper(20); break; + case 21: bits = laneHelper(21); break; + case 22: bits = laneHelper(22); break; + case 23: bits = laneHelper(23); break; + case 24: bits = laneHelper(24); break; + case 25: bits = laneHelper(25); break; + case 26: bits = laneHelper(26); break; + case 27: bits = laneHelper(27); break; + case 28: bits = laneHelper(28); break; + case 29: bits = laneHelper(29); break; + case 30: bits = laneHelper(30); break; + case 31: bits = laneHelper(31); break; + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + return bits; + } + + @ForceInline + public short laneHelper(int i) { + return (short) VectorSupport.extract( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, + (vec, ix) -> { + short[] vecarr = vec.vec(); + return vecarr[ix]; + }); + } + + @ForceInline + @Override + public Float16Vector512 withLane(int i, short e) { + switch(i) { + case 0: return withLaneHelper(0, e); + case 1: return withLaneHelper(1, e); + case 2: return withLaneHelper(2, e); + case 3: return withLaneHelper(3, e); + case 4: return withLaneHelper(4, e); + case 5: return withLaneHelper(5, e); + case 6: return withLaneHelper(6, e); + case 7: return withLaneHelper(7, e); + case 8: return withLaneHelper(8, e); + case 9: return withLaneHelper(9, e); + case 10: return withLaneHelper(10, e); + case 11: return withLaneHelper(11, e); + case 12: return withLaneHelper(12, e); + case 13: return withLaneHelper(13, e); + case 14: return withLaneHelper(14, e); + case 15: return withLaneHelper(15, e); + case 16: return withLaneHelper(16, e); + case 17: return withLaneHelper(17, e); + case 18: return withLaneHelper(18, e); + case 19: return withLaneHelper(19, e); + case 20: return withLaneHelper(20, e); + case 21: return withLaneHelper(21, e); + case 22: return withLaneHelper(22, e); + case 23: return withLaneHelper(23, e); + case 24: return withLaneHelper(24, e); + case 25: return withLaneHelper(25, e); + case 26: return withLaneHelper(26, e); + case 27: return withLaneHelper(27, e); + case 28: return withLaneHelper(28, e); + case 29: return withLaneHelper(29, e); + case 30: return withLaneHelper(30, e); + case 31: return withLaneHelper(31, e); + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + } + + @ForceInline + public Float16Vector512 withLaneHelper(int i, short e) { + return VectorSupport.insert( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (long)e, + (v, ix, bits) -> { + short[] res = v.vec().clone(); + res[ix] = (short)bits; + return v.vectorFactory(res); + }); + } + + // Mask + @ValueBased + static final class Float16Mask512 extends AbstractMask { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Mask512(boolean[] bits) { + this(bits, 0); + } + + Float16Mask512(boolean[] bits, int offset) { + super(prepare(bits, offset)); + } + + Float16Mask512(boolean val) { + super(prepare(val)); + } + + private static boolean[] prepare(boolean[] bits, int offset) { + boolean[] newBits = new boolean[VSPECIES.laneCount()]; + for (int i = 0; i < newBits.length; i++) { + newBits[i] = bits[offset + i]; + } + return newBits; + } + + private static boolean[] prepare(boolean val) { + boolean[] bits = new boolean[VSPECIES.laneCount()]; + Arrays.fill(bits, val); + return bits; + } + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractMask, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + boolean[] getBits() { + return (boolean[])getPayload(); + } + + @Override + Float16Mask512 uOp(MUnOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i]); + } + return new Float16Mask512(res); + } + + @Override + Float16Mask512 bOp(VectorMask m, MBinOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + boolean[] mbits = ((Float16Mask512)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i], mbits[i]); + } + return new Float16Mask512(res); + } + + @ForceInline + @Override + public final + Float16Vector512 toVector() { + return (Float16Vector512) super.toVectorTemplate(); // specialize + } + + /** + * Helper function for lane-wise mask conversions. + * This function kicks in after intrinsic failure. + */ + @ForceInline + private final + VectorMask defaultMaskCast(AbstractSpecies dsp) { + if (length() != dsp.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + boolean[] maskArray = toArray(); + return dsp.maskFactory(maskArray).check(dsp); + } + + @Override + @ForceInline + public VectorMask cast(VectorSpecies dsp) { + AbstractSpecies species = (AbstractSpecies) dsp; + if (length() != species.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + + return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, + this, species, + (m, s) -> s.maskFactory(m.toArray()).check(s)); + } + + @Override + @ForceInline + /*package-private*/ + Float16Mask512 indexPartiallyInUpperRange(long offset, long limit) { + return (Float16Mask512) VectorSupport.indexPartiallyInUpperRange( + Float16Mask512.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, + (o, l) -> (Float16Mask512) TRUE_MASK.indexPartiallyInRange(o, l)); + } + + // Unary operations + + @Override + @ForceInline + public Float16Mask512 not() { + return xor(maskAll(true)); + } + + @Override + @ForceInline + public Float16Mask512 compress() { + return (Float16Mask512)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, + Float16Vector512.class, Float16Mask512.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, + (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, Float16.float16ToRawShortBits(Float16.valueOf(m1.trueCount())))); + } + + + // Binary operations + + @Override + @ForceInline + public Float16Mask512 and(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask512 m = (Float16Mask512)mask; + return VectorSupport.binaryOp(VECTOR_OP_AND, Float16Mask512.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); + } + + @Override + @ForceInline + public Float16Mask512 or(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask512 m = (Float16Mask512)mask; + return VectorSupport.binaryOp(VECTOR_OP_OR, Float16Mask512.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); + } + + @Override + @ForceInline + public Float16Mask512 xor(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask512 m = (Float16Mask512)mask; + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float16Mask512.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); + } + + // Mask Query operations + + @Override + @ForceInline + public int trueCount() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> trueCountHelper(m.getBits())); + } + + @Override + @ForceInline + public int firstTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> firstTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public int lastTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> lastTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public long toLong() { + if (length() > Long.SIZE) { + throw new UnsupportedOperationException("too many lanes for one long"); + } + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> toLongHelper(m.getBits())); + } + + // laneIsSet + + @Override + @ForceInline + public boolean laneIsSet(int i) { + Objects.checkIndex(i, length()); + return VectorSupport.extract(Float16Mask512.class, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; + } + + // Reductions + + @Override + @ForceInline + public boolean anyTrue() { + return VectorSupport.test(BT_ne, Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((Float16Mask512)m).getBits())); + } + + @Override + @ForceInline + public boolean allTrue() { + return VectorSupport.test(BT_overflow, Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((Float16Mask512)m).getBits())); + } + + @ForceInline + /*package-private*/ + static Float16Mask512 maskAll(boolean bit) { + return VectorSupport.fromBitsCoerced(Float16Mask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + (bit ? -1 : 0), MODE_BROADCAST, null, + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + } + private static final Float16Mask512 TRUE_MASK = new Float16Mask512(true); + private static final Float16Mask512 FALSE_MASK = new Float16Mask512(false); + + } + + // Shuffle + @ValueBased + static final class Float16Shuffle512 extends AbstractShuffle { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Shuffle512(short[] indices) { + super(indices); + assert(VLENGTH == indices.length); + assert(indicesInRange(indices)); + } + + Float16Shuffle512(int[] indices, int i) { + this(prepare(indices, i)); + } + + Float16Shuffle512(IntUnaryOperator fn) { + this(prepare(fn)); + } + + short[] indices() { + return (short[])getPayload(); + } + + @Override + @ForceInline + public Float16Species vspecies() { + return VSPECIES; + } + + static { + // There must be enough bits in the shuffle lanes to encode + // VLENGTH valid indexes and VLENGTH exceptional ones. + assert(VLENGTH < Short.MAX_VALUE); + assert(Short.MIN_VALUE <= -VLENGTH); + } + static final Float16Shuffle512 IOTA = new Float16Shuffle512(IDENTITY); + + @Override + @ForceInline + public Float16Vector512 toVector() { + return (Float16Vector512) toBitsVector().castShape(VSPECIES, 0); + } + + @Override + @ForceInline + ShortVector512 toBitsVector() { + return (ShortVector512) super.toBitsVectorTemplate(); + } + + @Override + ShortVector512 toBitsVector0() { + return ((ShortVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); + } + + @Override + @ForceInline + public int laneSource(int i) { + return (int)toBitsVector().lane(i); + } + + @Override + @ForceInline + public void intoArray(int[] a, int offset) { + VectorSpecies species = IntVector.SPECIES_512; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoArray(a, offset); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoArray(a, offset + species.length()); + } + + @Override + @ForceInline + public void intoMemorySegment(MemorySegment ms, long offset, ByteOrder bo) { + VectorSpecies species = IntVector.SPECIES_512; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoMemorySegment(ms, offset, bo); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoMemorySegment(ms, offset + species.vectorByteSize(), bo); + } + + @Override + @ForceInline + public final Float16Mask512 laneIsValid() { + return (Float16Mask512) toBitsVector().compare(VectorOperators.GE, 0) + .cast(VSPECIES); + } + + @ForceInline + @Override + public final Float16Shuffle512 rearrange(VectorShuffle shuffle) { + Float16Shuffle512 concreteShuffle = (Float16Shuffle512) shuffle; + return (Float16Shuffle512) toBitsVector().rearrange(concreteShuffle.cast(ShortVector.SPECIES_512)) + .toShuffle(VSPECIES, false); + } + + @ForceInline + @Override + public final Float16Shuffle512 wrapIndexes() { + ShortVector512 v = toBitsVector(); + if ((length() & (length() - 1)) == 0) { + v = (ShortVector512) v.lanewise(VectorOperators.AND, length() - 1); + } else { + v = (ShortVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), + v.compare(VectorOperators.LT, 0)); + } + return (Float16Shuffle512) v.toShuffle(VSPECIES, false); + } + + private static short[] prepare(int[] indices, int offset) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = indices[offset + i]; + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static short[] prepare(IntUnaryOperator f) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = f.applyAsInt(i); + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static boolean indicesInRange(short[] indices) { + int length = indices.length; + for (short si : indices) { + if (si >= (short)length || si < (short)(-length)) { + String msg = ("index "+si+"out of range ["+length+"] in "+ + java.util.Arrays.toString(indices)); + throw new AssertionError(msg); + } + } + return true; + } + } + + // ================================================ + + // Specialized low-level memory operations. + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset) { + return super.fromArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, VectorMask m, int offsetInRange) { + return super.fromArray0Template(Float16Mask512.class, a, offset, (Float16Mask512) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, int[] indexMap, int mapOffset, VectorMask m) { + return super.fromArray0Template(Float16Mask512.class, a, offset, indexMap, mapOffset, (Float16Mask512) m); + } + + + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset) { + return super.fromMemorySegment0Template(ms, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset, VectorMask m, int offsetInRange) { + return super.fromMemorySegment0Template(Float16Mask512.class, ms, offset, (Float16Mask512) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset) { + super.intoArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset, VectorMask m) { + super.intoArray0Template(Float16Mask512.class, a, offset, (Float16Mask512) m); + } + + + + @ForceInline + @Override + final + void intoMemorySegment0(MemorySegment ms, long offset, VectorMask m) { + super.intoMemorySegment0Template(Float16Mask512.class, ms, offset, (Float16Mask512) m); + } + + + // End of specialized low-level memory operations. + + // ================================================ + +} + diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector64.java new file mode 100644 index 00000000000..973e5261618 --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector64.java @@ -0,0 +1,1029 @@ +/* + * 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 + * 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 jdk.incubator.vector; + +import java.lang.foreign.MemorySegment; +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.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 Float16Vector64 extends Float16Vector { + static final Float16Species VSPECIES = + (Float16Species) Float16Vector.SPECIES_64; + + static final VectorShape VSHAPE = + VSPECIES.vectorShape(); + + static final Class VCLASS = Float16Vector64.class; + + static final int VSIZE = VSPECIES.vectorBitSize(); + + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // carrier type used by the JVM + + static final Class ETYPE = Float16.class; // used by the JVM + + Float16Vector64(short[] v) { + super(v); + } + + // For compatibility as Float16Vector64::new, + // stored into species.vectorFactory. + Float16Vector64(Object v) { + this((short[]) v); + } + + static final Float16Vector64 ZERO = new Float16Vector64(new short[VLENGTH]); + static final Float16Vector64 IOTA = new Float16Vector64(VSPECIES.iotaArray()); + + static { + // Warm up a few species caches. + // If we do this too much we will + // get NPEs from bootstrap circularity. + VSPECIES.dummyVector(); + VSPECIES.withLanes(LaneType.BYTE); + } + + // Specialized extractors + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractVector, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + @Override + public final Class elementType() { return Float16.class; } + + @ForceInline + final Class carrierType() { return CTYPE; } + + @ForceInline + @Override + public final int elementSize() { return Float16.SIZE; } + + @ForceInline + @Override + public final VectorShape shape() { return VSHAPE; } + + @ForceInline + @Override + public final int length() { return VLENGTH; } + + @ForceInline + @Override + public final int bitSize() { return VSIZE; } + + @ForceInline + @Override + public final int byteSize() { return VSIZE / Byte.SIZE; } + + /*package-private*/ + @ForceInline + final @Override + short[] vec() { + return (short[])getPayload(); + } + + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + + // Virtualized constructors + + @Override + @ForceInline + public final Float16Vector64 broadcast(short e) { + return (Float16Vector64) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + public final Float16Vector64 broadcast(long e) { + return (Float16Vector64) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + Float16Mask64 maskFromArray(boolean[] bits) { + return new Float16Mask64(bits); + } + + @Override + @ForceInline + Float16Shuffle64 iotaShuffle() { return Float16Shuffle64.IOTA; } + + @Override + @ForceInline + Float16Shuffle64 iotaShuffle(int start, int step, boolean wrap) { + return (Float16Shuffle64) iotaShuffleTemplate((short) start, (short) step, wrap); + } + + @Override + @ForceInline + Float16Shuffle64 shuffleFromArray(int[] indices, int i) { return new Float16Shuffle64(indices, i); } + + @Override + @ForceInline + Float16Shuffle64 shuffleFromOp(IntUnaryOperator fn) { return new Float16Shuffle64(fn); } + + // Make a vector of the same species but the given elements: + @ForceInline + final @Override + Float16Vector64 vectorFactory(short[] vec) { + return new Float16Vector64(vec); + } + + @ForceInline + final @Override + ByteVector64 asByteVectorRaw() { + return (ByteVector64) super.asByteVectorRawTemplate(); // specialize + } + + @ForceInline + final @Override + AbstractVector asVectorRaw(LaneType laneType) { + return super.asVectorRawTemplate(laneType); // specialize + } + + // Unary operator + + @ForceInline + final @Override + Float16Vector64 uOp(FUnOp f) { + return (Float16Vector64) super.uOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector64 uOp(VectorMask m, FUnOp f) { + return (Float16Vector64) + super.uOpTemplate((Float16Mask64)m, f); // specialize + } + + @ForceInline + final @Override + Float16Vector64 uRawOp(FUnRawOp f) { + return (Float16Vector64) super.uRawOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16Vector64 uRawOp(VectorMask m, FUnRawOp f) { + return (Float16Vector64) + super.uRawOpTemplate((Float16Mask64)m, f); // specialize + } + // Binary operator + + @ForceInline + final @Override + Float16Vector64 bOp(Vector v, FBinOp f) { + return (Float16Vector64) super.bOpTemplate((Float16Vector64)v, f); // specialize + } + + @ForceInline + final @Override + Float16Vector64 bOp(Vector v, + VectorMask m, FBinOp f) { + return (Float16Vector64) + super.bOpTemplate((Float16Vector64)v, (Float16Mask64)m, + f); // specialize + } + + // Ternary operator + + @ForceInline + final @Override + Float16Vector64 tOp(Vector v1, Vector v2, FTriOp f) { + return (Float16Vector64) + super.tOpTemplate((Float16Vector64)v1, (Float16Vector64)v2, + f); // specialize + } + + @ForceInline + final @Override + Float16Vector64 tOp(Vector v1, Vector v2, + VectorMask m, FTriOp f) { + return (Float16Vector64) + super.tOpTemplate((Float16Vector64)v1, (Float16Vector64)v2, + (Float16Mask64)m, f); // specialize + } + + @ForceInline + final @Override + short rOp(short v, VectorMask m, FBinOp f) { + return super.rOpTemplate(v, m, f); // specialize + } + + @Override + @ForceInline + public final + Vector convertShape(VectorOperators.Conversion conv, + VectorSpecies rsp, int part) { + return super.convertShapeTemplate(conv, rsp, part); // specialize + } + + @Override + @ForceInline + public final + Vector reinterpretShape(VectorSpecies toSpecies, int part) { + return super.reinterpretShapeTemplate(toSpecies, part); // specialize + } + + // Specialized algebraic operations: + + // The following definition forces a specialized version of this + // crucial method into the v-table of this class. A call to add() + // will inline to a call to lanewise(ADD,), at which point the JIT + // intrinsic will have the opcode of ADD, plus all the metadata + // for this particular class, enabling it to generate precise + // code. + // + // There is probably no benefit to the JIT to specialize the + // masked or broadcast versions of the lanewise method. + + @Override + @ForceInline + public Float16Vector64 lanewise(Unary op) { + return (Float16Vector64) super.lanewiseTemplate(op); // specialize + } + + @Override + @ForceInline + public Float16Vector64 lanewise(Unary op, VectorMask m) { + return (Float16Vector64) super.lanewiseTemplate(op, Float16Mask64.class, (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 lanewise(Binary op, Vector v) { + return (Float16Vector64) super.lanewiseTemplate(op, v); // specialize + } + + @Override + @ForceInline + public Float16Vector64 lanewise(Binary op, Vector v, VectorMask m) { + return (Float16Vector64) super.lanewiseTemplate(op, Float16Mask64.class, v, (Float16Mask64) m); // specialize + } + + + /*package-private*/ + @Override + @ForceInline + public final + Float16Vector64 + lanewise(Ternary op, Vector v1, Vector v2) { + return (Float16Vector64) super.lanewiseTemplate(op, v1, v2); // specialize + } + + @Override + @ForceInline + public final + Float16Vector64 + lanewise(Ternary op, Vector v1, Vector v2, VectorMask m) { + return (Float16Vector64) super.lanewiseTemplate(op, Float16Mask64.class, v1, v2, (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public final + Float16Vector64 addIndex(int scale) { + return (Float16Vector64) super.addIndexTemplate(scale); // specialize + } + + // Type specific horizontal reductions + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op) { + return super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op, + VectorMask m) { + return super.reduceLanesTemplate(op, Float16Mask64.class, (Float16Mask64) m); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op) { + return (long) super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op, + VectorMask m) { + return (long) super.reduceLanesTemplate(op, Float16Mask64.class, (Float16Mask64) m); // specialized + } + + @Override + @ForceInline + final VectorShuffle bitsToShuffle(AbstractSpecies dsp) { + throw new AssertionError(); + } + + @Override + @ForceInline + public final Float16Shuffle64 toShuffle() { + return (Float16Shuffle64) toShuffle(VSPECIES, false); + } + + // Specialized unary testing + + @Override + @ForceInline + public final Float16Mask64 test(Test op) { + return super.testTemplate(Float16Mask64.class, op); // specialize + } + + @Override + @ForceInline + public final Float16Mask64 test(Test op, VectorMask m) { + return super.testTemplate(Float16Mask64.class, op, (Float16Mask64) m); // specialize + } + + // Specialized comparisons + + @Override + @ForceInline + public final Float16Mask64 compare(Comparison op, Vector v) { + return super.compareTemplate(Float16Mask64.class, op, v); // specialize + } + + @Override + @ForceInline + public final Float16Mask64 compare(Comparison op, short s) { + return super.compareTemplate(Float16Mask64.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask64 compare(Comparison op, long s) { + return super.compareTemplate(Float16Mask64.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16Mask64 compare(Comparison op, Vector v, VectorMask m) { + return super.compareTemplate(Float16Mask64.class, op, v, (Float16Mask64) m); + } + + + @Override + @ForceInline + public Float16Vector64 blend(Vector v, VectorMask m) { + return (Float16Vector64) + super.blendTemplate(Float16Mask64.class, + (Float16Vector64) v, + (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 slice(int origin, Vector v) { + return (Float16Vector64) super.sliceTemplate(origin, v); // specialize + } + + @Override + @ForceInline + public Float16Vector64 slice(int origin) { + return (Float16Vector64) super.sliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector64 unslice(int origin, Vector w, int part) { + return (Float16Vector64) super.unsliceTemplate(origin, w, part); // specialize + } + + @Override + @ForceInline + public Float16Vector64 unslice(int origin, Vector w, int part, VectorMask m) { + return (Float16Vector64) + super.unsliceTemplate(Float16Mask64.class, + origin, w, part, + (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 unslice(int origin) { + return (Float16Vector64) super.unsliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16Vector64 rearrange(VectorShuffle s) { + return (Float16Vector64) + super.rearrangeTemplate(Float16Shuffle64.class, + (Float16Shuffle64) s); // specialize + } + + @Override + @ForceInline + public Float16Vector64 rearrange(VectorShuffle shuffle, + VectorMask m) { + return (Float16Vector64) + super.rearrangeTemplate(Float16Shuffle64.class, + Float16Mask64.class, + (Float16Shuffle64) shuffle, + (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 rearrange(VectorShuffle s, + Vector v) { + return (Float16Vector64) + super.rearrangeTemplate(Float16Shuffle64.class, + (Float16Shuffle64) s, + (Float16Vector64) v); // specialize + } + + @Override + @ForceInline + public Float16Vector64 compress(VectorMask m) { + return (Float16Vector64) + super.compressTemplate(Float16Mask64.class, + (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 expand(VectorMask m) { + return (Float16Vector64) + super.expandTemplate(Float16Mask64.class, + (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 selectFrom(Vector v) { + return (Float16Vector64) + super.selectFromTemplate((Float16Vector64) v); // specialize + } + + @Override + @ForceInline + public Float16Vector64 selectFrom(Vector v, + VectorMask m) { + return (Float16Vector64) + super.selectFromTemplate((Float16Vector64) v, + Float16Mask64.class, (Float16Mask64) m); // specialize + } + + @Override + @ForceInline + public Float16Vector64 selectFrom(Vector v1, + Vector v2) { + return (Float16Vector64) + super.selectFromTemplate((Float16Vector64) v1, (Float16Vector64) v2); // specialize + } + + @ForceInline + @Override + public short lane(int i) { + short bits; + switch(i) { + case 0: bits = laneHelper(0); break; + case 1: bits = laneHelper(1); break; + case 2: bits = laneHelper(2); break; + case 3: bits = laneHelper(3); break; + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + return bits; + } + + @ForceInline + public short laneHelper(int i) { + return (short) VectorSupport.extract( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, + (vec, ix) -> { + short[] vecarr = vec.vec(); + return vecarr[ix]; + }); + } + + @ForceInline + @Override + public Float16Vector64 withLane(int i, short e) { + switch(i) { + case 0: return withLaneHelper(0, e); + case 1: return withLaneHelper(1, e); + case 2: return withLaneHelper(2, e); + case 3: return withLaneHelper(3, e); + default: throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + } + + @ForceInline + public Float16Vector64 withLaneHelper(int i, short e) { + return VectorSupport.insert( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (long)e, + (v, ix, bits) -> { + short[] res = v.vec().clone(); + res[ix] = (short)bits; + return v.vectorFactory(res); + }); + } + + // Mask + @ValueBased + static final class Float16Mask64 extends AbstractMask { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Mask64(boolean[] bits) { + this(bits, 0); + } + + Float16Mask64(boolean[] bits, int offset) { + super(prepare(bits, offset)); + } + + Float16Mask64(boolean val) { + super(prepare(val)); + } + + private static boolean[] prepare(boolean[] bits, int offset) { + boolean[] newBits = new boolean[VSPECIES.laneCount()]; + for (int i = 0; i < newBits.length; i++) { + newBits[i] = bits[offset + i]; + } + return newBits; + } + + private static boolean[] prepare(boolean val) { + boolean[] bits = new boolean[VSPECIES.laneCount()]; + Arrays.fill(bits, val); + return bits; + } + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractMask, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + boolean[] getBits() { + return (boolean[])getPayload(); + } + + @Override + Float16Mask64 uOp(MUnOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i]); + } + return new Float16Mask64(res); + } + + @Override + Float16Mask64 bOp(VectorMask m, MBinOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + boolean[] mbits = ((Float16Mask64)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i], mbits[i]); + } + return new Float16Mask64(res); + } + + @ForceInline + @Override + public final + Float16Vector64 toVector() { + return (Float16Vector64) super.toVectorTemplate(); // specialize + } + + /** + * Helper function for lane-wise mask conversions. + * This function kicks in after intrinsic failure. + */ + @ForceInline + private final + VectorMask defaultMaskCast(AbstractSpecies dsp) { + if (length() != dsp.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + boolean[] maskArray = toArray(); + return dsp.maskFactory(maskArray).check(dsp); + } + + @Override + @ForceInline + public VectorMask cast(VectorSpecies dsp) { + AbstractSpecies species = (AbstractSpecies) dsp; + if (length() != species.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + + return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, + this, species, + (m, s) -> s.maskFactory(m.toArray()).check(s)); + } + + @Override + @ForceInline + /*package-private*/ + Float16Mask64 indexPartiallyInUpperRange(long offset, long limit) { + return (Float16Mask64) VectorSupport.indexPartiallyInUpperRange( + Float16Mask64.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, + (o, l) -> (Float16Mask64) TRUE_MASK.indexPartiallyInRange(o, l)); + } + + // Unary operations + + @Override + @ForceInline + public Float16Mask64 not() { + return xor(maskAll(true)); + } + + @Override + @ForceInline + public Float16Mask64 compress() { + return (Float16Mask64)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, + Float16Vector64.class, Float16Mask64.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, + (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, Float16.float16ToRawShortBits(Float16.valueOf(m1.trueCount())))); + } + + + // Binary operations + + @Override + @ForceInline + public Float16Mask64 and(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask64 m = (Float16Mask64)mask; + return VectorSupport.binaryOp(VECTOR_OP_AND, Float16Mask64.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); + } + + @Override + @ForceInline + public Float16Mask64 or(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask64 m = (Float16Mask64)mask; + return VectorSupport.binaryOp(VECTOR_OP_OR, Float16Mask64.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); + } + + @Override + @ForceInline + public Float16Mask64 xor(VectorMask mask) { + Objects.requireNonNull(mask); + Float16Mask64 m = (Float16Mask64)mask; + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float16Mask64.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); + } + + // Mask Query operations + + @Override + @ForceInline + public int trueCount() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> trueCountHelper(m.getBits())); + } + + @Override + @ForceInline + public int firstTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> firstTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public int lastTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> lastTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public long toLong() { + if (length() > Long.SIZE) { + throw new UnsupportedOperationException("too many lanes for one long"); + } + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> toLongHelper(m.getBits())); + } + + // laneIsSet + + @Override + @ForceInline + public boolean laneIsSet(int i) { + Objects.checkIndex(i, length()); + return VectorSupport.extract(Float16Mask64.class, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; + } + + // Reductions + + @Override + @ForceInline + public boolean anyTrue() { + return VectorSupport.test(BT_ne, Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((Float16Mask64)m).getBits())); + } + + @Override + @ForceInline + public boolean allTrue() { + return VectorSupport.test(BT_overflow, Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((Float16Mask64)m).getBits())); + } + + @ForceInline + /*package-private*/ + static Float16Mask64 maskAll(boolean bit) { + return VectorSupport.fromBitsCoerced(Float16Mask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + (bit ? -1 : 0), MODE_BROADCAST, null, + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + } + private static final Float16Mask64 TRUE_MASK = new Float16Mask64(true); + private static final Float16Mask64 FALSE_MASK = new Float16Mask64(false); + + } + + // Shuffle + @ValueBased + static final class Float16Shuffle64 extends AbstractShuffle { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16Shuffle64(short[] indices) { + super(indices); + assert(VLENGTH == indices.length); + assert(indicesInRange(indices)); + } + + Float16Shuffle64(int[] indices, int i) { + this(prepare(indices, i)); + } + + Float16Shuffle64(IntUnaryOperator fn) { + this(prepare(fn)); + } + + short[] indices() { + return (short[])getPayload(); + } + + @Override + @ForceInline + public Float16Species vspecies() { + return VSPECIES; + } + + static { + // There must be enough bits in the shuffle lanes to encode + // VLENGTH valid indexes and VLENGTH exceptional ones. + assert(VLENGTH < Short.MAX_VALUE); + assert(Short.MIN_VALUE <= -VLENGTH); + } + static final Float16Shuffle64 IOTA = new Float16Shuffle64(IDENTITY); + + @Override + @ForceInline + public Float16Vector64 toVector() { + return (Float16Vector64) toBitsVector().castShape(VSPECIES, 0); + } + + @Override + @ForceInline + ShortVector64 toBitsVector() { + return (ShortVector64) super.toBitsVectorTemplate(); + } + + @Override + ShortVector64 toBitsVector0() { + return ((ShortVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); + } + + @Override + @ForceInline + public int laneSource(int i) { + return (int)toBitsVector().lane(i); + } + + @Override + @ForceInline + public void intoArray(int[] a, int offset) { + VectorSpecies species = IntVector.SPECIES_64; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoArray(a, offset); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoArray(a, offset + species.length()); + } + + @Override + @ForceInline + public void intoMemorySegment(MemorySegment ms, long offset, ByteOrder bo) { + VectorSpecies species = IntVector.SPECIES_64; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoMemorySegment(ms, offset, bo); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoMemorySegment(ms, offset + species.vectorByteSize(), bo); + } + + @Override + @ForceInline + public final Float16Mask64 laneIsValid() { + return (Float16Mask64) toBitsVector().compare(VectorOperators.GE, 0) + .cast(VSPECIES); + } + + @ForceInline + @Override + public final Float16Shuffle64 rearrange(VectorShuffle shuffle) { + Float16Shuffle64 concreteShuffle = (Float16Shuffle64) shuffle; + return (Float16Shuffle64) toBitsVector().rearrange(concreteShuffle.cast(ShortVector.SPECIES_64)) + .toShuffle(VSPECIES, false); + } + + @ForceInline + @Override + public final Float16Shuffle64 wrapIndexes() { + ShortVector64 v = toBitsVector(); + if ((length() & (length() - 1)) == 0) { + v = (ShortVector64) v.lanewise(VectorOperators.AND, length() - 1); + } else { + v = (ShortVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), + v.compare(VectorOperators.LT, 0)); + } + return (Float16Shuffle64) v.toShuffle(VSPECIES, false); + } + + private static short[] prepare(int[] indices, int offset) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = indices[offset + i]; + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static short[] prepare(IntUnaryOperator f) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = f.applyAsInt(i); + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static boolean indicesInRange(short[] indices) { + int length = indices.length; + for (short si : indices) { + if (si >= (short)length || si < (short)(-length)) { + String msg = ("index "+si+"out of range ["+length+"] in "+ + java.util.Arrays.toString(indices)); + throw new AssertionError(msg); + } + } + return true; + } + } + + // ================================================ + + // Specialized low-level memory operations. + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset) { + return super.fromArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, VectorMask m, int offsetInRange) { + return super.fromArray0Template(Float16Mask64.class, a, offset, (Float16Mask64) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, int[] indexMap, int mapOffset, VectorMask m) { + return super.fromArray0Template(Float16Mask64.class, a, offset, indexMap, mapOffset, (Float16Mask64) m); + } + + + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset) { + return super.fromMemorySegment0Template(ms, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset, VectorMask m, int offsetInRange) { + return super.fromMemorySegment0Template(Float16Mask64.class, ms, offset, (Float16Mask64) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset) { + super.intoArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset, VectorMask m) { + super.intoArray0Template(Float16Mask64.class, a, offset, (Float16Mask64) m); + } + + + + @ForceInline + @Override + final + void intoMemorySegment0(MemorySegment ms, long offset, VectorMask m) { + super.intoMemorySegment0Template(Float16Mask64.class, ms, offset, (Float16Mask64) m); + } + + + // End of specialized low-level memory operations. + + // ================================================ + +} + diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16VectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16VectorMax.java new file mode 100644 index 00000000000..3de14504f86 --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16VectorMax.java @@ -0,0 +1,1022 @@ +/* + * 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 + * 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 jdk.incubator.vector; + +import java.lang.foreign.MemorySegment; +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.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 Float16VectorMax extends Float16Vector { + static final Float16Species VSPECIES = + (Float16Species) Float16Vector.SPECIES_MAX; + + static final VectorShape VSHAPE = + VSPECIES.vectorShape(); + + static final Class VCLASS = Float16VectorMax.class; + + static final int VSIZE = VSPECIES.vectorBitSize(); + + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // carrier type used by the JVM + + static final Class ETYPE = Float16.class; // used by the JVM + + Float16VectorMax(short[] v) { + super(v); + } + + // For compatibility as Float16VectorMax::new, + // stored into species.vectorFactory. + Float16VectorMax(Object v) { + this((short[]) v); + } + + static final Float16VectorMax ZERO = new Float16VectorMax(new short[VLENGTH]); + static final Float16VectorMax IOTA = new Float16VectorMax(VSPECIES.iotaArray()); + + static { + // Warm up a few species caches. + // If we do this too much we will + // get NPEs from bootstrap circularity. + VSPECIES.dummyVector(); + VSPECIES.withLanes(LaneType.BYTE); + } + + // Specialized extractors + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractVector, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + @Override + public final Class elementType() { return Float16.class; } + + @ForceInline + final Class carrierType() { return CTYPE; } + + @ForceInline + @Override + public final int elementSize() { return Float16.SIZE; } + + @ForceInline + @Override + public final VectorShape shape() { return VSHAPE; } + + @ForceInline + @Override + public final int length() { return VLENGTH; } + + @ForceInline + @Override + public final int bitSize() { return VSIZE; } + + @ForceInline + @Override + public final int byteSize() { return VSIZE / Byte.SIZE; } + + /*package-private*/ + @ForceInline + final @Override + short[] vec() { + return (short[])getPayload(); + } + + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + + // Virtualized constructors + + @Override + @ForceInline + public final Float16VectorMax broadcast(short e) { + return (Float16VectorMax) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + public final Float16VectorMax broadcast(long e) { + return (Float16VectorMax) super.broadcastTemplate(e); // specialize + } + + @Override + @ForceInline + Float16MaskMax maskFromArray(boolean[] bits) { + return new Float16MaskMax(bits); + } + + @Override + @ForceInline + Float16ShuffleMax iotaShuffle() { return Float16ShuffleMax.IOTA; } + + @Override + @ForceInline + Float16ShuffleMax iotaShuffle(int start, int step, boolean wrap) { + return (Float16ShuffleMax) iotaShuffleTemplate((short) start, (short) step, wrap); + } + + @Override + @ForceInline + Float16ShuffleMax shuffleFromArray(int[] indices, int i) { return new Float16ShuffleMax(indices, i); } + + @Override + @ForceInline + Float16ShuffleMax shuffleFromOp(IntUnaryOperator fn) { return new Float16ShuffleMax(fn); } + + // Make a vector of the same species but the given elements: + @ForceInline + final @Override + Float16VectorMax vectorFactory(short[] vec) { + return new Float16VectorMax(vec); + } + + @ForceInline + final @Override + ByteVectorMax asByteVectorRaw() { + return (ByteVectorMax) super.asByteVectorRawTemplate(); // specialize + } + + @ForceInline + final @Override + AbstractVector asVectorRaw(LaneType laneType) { + return super.asVectorRawTemplate(laneType); // specialize + } + + // Unary operator + + @ForceInline + final @Override + Float16VectorMax uOp(FUnOp f) { + return (Float16VectorMax) super.uOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16VectorMax uOp(VectorMask m, FUnOp f) { + return (Float16VectorMax) + super.uOpTemplate((Float16MaskMax)m, f); // specialize + } + + @ForceInline + final @Override + Float16VectorMax uRawOp(FUnRawOp f) { + return (Float16VectorMax) super.uRawOpTemplate(f); // specialize + } + + @ForceInline + final @Override + Float16VectorMax uRawOp(VectorMask m, FUnRawOp f) { + return (Float16VectorMax) + super.uRawOpTemplate((Float16MaskMax)m, f); // specialize + } + // Binary operator + + @ForceInline + final @Override + Float16VectorMax bOp(Vector v, FBinOp f) { + return (Float16VectorMax) super.bOpTemplate((Float16VectorMax)v, f); // specialize + } + + @ForceInline + final @Override + Float16VectorMax bOp(Vector v, + VectorMask m, FBinOp f) { + return (Float16VectorMax) + super.bOpTemplate((Float16VectorMax)v, (Float16MaskMax)m, + f); // specialize + } + + // Ternary operator + + @ForceInline + final @Override + Float16VectorMax tOp(Vector v1, Vector v2, FTriOp f) { + return (Float16VectorMax) + super.tOpTemplate((Float16VectorMax)v1, (Float16VectorMax)v2, + f); // specialize + } + + @ForceInline + final @Override + Float16VectorMax tOp(Vector v1, Vector v2, + VectorMask m, FTriOp f) { + return (Float16VectorMax) + super.tOpTemplate((Float16VectorMax)v1, (Float16VectorMax)v2, + (Float16MaskMax)m, f); // specialize + } + + @ForceInline + final @Override + short rOp(short v, VectorMask m, FBinOp f) { + return super.rOpTemplate(v, m, f); // specialize + } + + @Override + @ForceInline + public final + Vector convertShape(VectorOperators.Conversion conv, + VectorSpecies rsp, int part) { + return super.convertShapeTemplate(conv, rsp, part); // specialize + } + + @Override + @ForceInline + public final + Vector reinterpretShape(VectorSpecies toSpecies, int part) { + return super.reinterpretShapeTemplate(toSpecies, part); // specialize + } + + // Specialized algebraic operations: + + // The following definition forces a specialized version of this + // crucial method into the v-table of this class. A call to add() + // will inline to a call to lanewise(ADD,), at which point the JIT + // intrinsic will have the opcode of ADD, plus all the metadata + // for this particular class, enabling it to generate precise + // code. + // + // There is probably no benefit to the JIT to specialize the + // masked or broadcast versions of the lanewise method. + + @Override + @ForceInline + public Float16VectorMax lanewise(Unary op) { + return (Float16VectorMax) super.lanewiseTemplate(op); // specialize + } + + @Override + @ForceInline + public Float16VectorMax lanewise(Unary op, VectorMask m) { + return (Float16VectorMax) super.lanewiseTemplate(op, Float16MaskMax.class, (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax lanewise(Binary op, Vector v) { + return (Float16VectorMax) super.lanewiseTemplate(op, v); // specialize + } + + @Override + @ForceInline + public Float16VectorMax lanewise(Binary op, Vector v, VectorMask m) { + return (Float16VectorMax) super.lanewiseTemplate(op, Float16MaskMax.class, v, (Float16MaskMax) m); // specialize + } + + + /*package-private*/ + @Override + @ForceInline + public final + Float16VectorMax + lanewise(Ternary op, Vector v1, Vector v2) { + return (Float16VectorMax) super.lanewiseTemplate(op, v1, v2); // specialize + } + + @Override + @ForceInline + public final + Float16VectorMax + lanewise(Ternary op, Vector v1, Vector v2, VectorMask m) { + return (Float16VectorMax) super.lanewiseTemplate(op, Float16MaskMax.class, v1, v2, (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public final + Float16VectorMax addIndex(int scale) { + return (Float16VectorMax) super.addIndexTemplate(scale); // specialize + } + + // Type specific horizontal reductions + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op) { + return super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final short reduceLanes(VectorOperators.Associative op, + VectorMask m) { + return super.reduceLanesTemplate(op, Float16MaskMax.class, (Float16MaskMax) m); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op) { + return (long) super.reduceLanesTemplate(op); // specialized + } + + @Override + @ForceInline + public final long reduceLanesToLong(VectorOperators.Associative op, + VectorMask m) { + return (long) super.reduceLanesTemplate(op, Float16MaskMax.class, (Float16MaskMax) m); // specialized + } + + @Override + @ForceInline + final VectorShuffle bitsToShuffle(AbstractSpecies dsp) { + throw new AssertionError(); + } + + @Override + @ForceInline + public final Float16ShuffleMax toShuffle() { + return (Float16ShuffleMax) toShuffle(VSPECIES, false); + } + + // Specialized unary testing + + @Override + @ForceInline + public final Float16MaskMax test(Test op) { + return super.testTemplate(Float16MaskMax.class, op); // specialize + } + + @Override + @ForceInline + public final Float16MaskMax test(Test op, VectorMask m) { + return super.testTemplate(Float16MaskMax.class, op, (Float16MaskMax) m); // specialize + } + + // Specialized comparisons + + @Override + @ForceInline + public final Float16MaskMax compare(Comparison op, Vector v) { + return super.compareTemplate(Float16MaskMax.class, op, v); // specialize + } + + @Override + @ForceInline + public final Float16MaskMax compare(Comparison op, short s) { + return super.compareTemplate(Float16MaskMax.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16MaskMax compare(Comparison op, long s) { + return super.compareTemplate(Float16MaskMax.class, op, s); // specialize + } + + @Override + @ForceInline + public final Float16MaskMax compare(Comparison op, Vector v, VectorMask m) { + return super.compareTemplate(Float16MaskMax.class, op, v, (Float16MaskMax) m); + } + + + @Override + @ForceInline + public Float16VectorMax blend(Vector v, VectorMask m) { + return (Float16VectorMax) + super.blendTemplate(Float16MaskMax.class, + (Float16VectorMax) v, + (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax slice(int origin, Vector v) { + return (Float16VectorMax) super.sliceTemplate(origin, v); // specialize + } + + @Override + @ForceInline + public Float16VectorMax slice(int origin) { + return (Float16VectorMax) super.sliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16VectorMax unslice(int origin, Vector w, int part) { + return (Float16VectorMax) super.unsliceTemplate(origin, w, part); // specialize + } + + @Override + @ForceInline + public Float16VectorMax unslice(int origin, Vector w, int part, VectorMask m) { + return (Float16VectorMax) + super.unsliceTemplate(Float16MaskMax.class, + origin, w, part, + (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax unslice(int origin) { + return (Float16VectorMax) super.unsliceTemplate(origin); // specialize + } + + @Override + @ForceInline + public Float16VectorMax rearrange(VectorShuffle s) { + return (Float16VectorMax) + super.rearrangeTemplate(Float16ShuffleMax.class, + (Float16ShuffleMax) s); // specialize + } + + @Override + @ForceInline + public Float16VectorMax rearrange(VectorShuffle shuffle, + VectorMask m) { + return (Float16VectorMax) + super.rearrangeTemplate(Float16ShuffleMax.class, + Float16MaskMax.class, + (Float16ShuffleMax) shuffle, + (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax rearrange(VectorShuffle s, + Vector v) { + return (Float16VectorMax) + super.rearrangeTemplate(Float16ShuffleMax.class, + (Float16ShuffleMax) s, + (Float16VectorMax) v); // specialize + } + + @Override + @ForceInline + public Float16VectorMax compress(VectorMask m) { + return (Float16VectorMax) + super.compressTemplate(Float16MaskMax.class, + (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax expand(VectorMask m) { + return (Float16VectorMax) + super.expandTemplate(Float16MaskMax.class, + (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax selectFrom(Vector v) { + return (Float16VectorMax) + super.selectFromTemplate((Float16VectorMax) v); // specialize + } + + @Override + @ForceInline + public Float16VectorMax selectFrom(Vector v, + VectorMask m) { + return (Float16VectorMax) + super.selectFromTemplate((Float16VectorMax) v, + Float16MaskMax.class, (Float16MaskMax) m); // specialize + } + + @Override + @ForceInline + public Float16VectorMax selectFrom(Vector v1, + Vector v2) { + return (Float16VectorMax) + super.selectFromTemplate((Float16VectorMax) v1, (Float16VectorMax) v2); // specialize + } + + @ForceInline + @Override + public short lane(int i) { + if (i < 0 || i >= VLENGTH) { + throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + short bits = laneHelper(i); + return bits; + } + + @ForceInline + public short laneHelper(int i) { + return (short) VectorSupport.extract( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, + (vec, ix) -> { + short[] vecarr = vec.vec(); + return vecarr[ix]; + }); + } + + @ForceInline + @Override + public Float16VectorMax withLane(int i, short e) { + if (i < 0 || i >= VLENGTH) { + throw new IllegalArgumentException("Index " + i + " must be zero or positive, and less than " + VLENGTH); + } + return withLaneHelper(i, e); + } + + @ForceInline + public Float16VectorMax withLaneHelper(int i, short e) { + return VectorSupport.insert( + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (long)e, + (v, ix, bits) -> { + short[] res = v.vec().clone(); + res[ix] = (short)bits; + return v.vectorFactory(res); + }); + } + + // Mask + @ValueBased + static final class Float16MaskMax extends AbstractMask { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16MaskMax(boolean[] bits) { + this(bits, 0); + } + + Float16MaskMax(boolean[] bits, int offset) { + super(prepare(bits, offset)); + } + + Float16MaskMax(boolean val) { + super(prepare(val)); + } + + private static boolean[] prepare(boolean[] bits, int offset) { + boolean[] newBits = new boolean[VSPECIES.laneCount()]; + for (int i = 0; i < newBits.length; i++) { + newBits[i] = bits[offset + i]; + } + return newBits; + } + + private static boolean[] prepare(boolean val) { + boolean[] bits = new boolean[VSPECIES.laneCount()]; + Arrays.fill(bits, val); + return bits; + } + + @ForceInline + final @Override + public Float16Species vspecies() { + // ISSUE: This should probably be a @Stable + // field inside AbstractMask, rather than + // a megamorphic method. + return VSPECIES; + } + + @ForceInline + boolean[] getBits() { + return (boolean[])getPayload(); + } + + @Override + Float16MaskMax uOp(MUnOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i]); + } + return new Float16MaskMax(res); + } + + @Override + Float16MaskMax bOp(VectorMask m, MBinOp f) { + boolean[] res = new boolean[VSPECIES.laneCount()]; + boolean[] bits = getBits(); + boolean[] mbits = ((Float16MaskMax)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, bits[i], mbits[i]); + } + return new Float16MaskMax(res); + } + + @ForceInline + @Override + public final + Float16VectorMax toVector() { + return (Float16VectorMax) super.toVectorTemplate(); // specialize + } + + /** + * Helper function for lane-wise mask conversions. + * This function kicks in after intrinsic failure. + */ + @ForceInline + private final + VectorMask defaultMaskCast(AbstractSpecies dsp) { + if (length() != dsp.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + boolean[] maskArray = toArray(); + return dsp.maskFactory(maskArray).check(dsp); + } + + @Override + @ForceInline + public VectorMask cast(VectorSpecies dsp) { + AbstractSpecies species = (AbstractSpecies) dsp; + if (length() != species.laneCount()) + throw new IllegalArgumentException("VectorMask length and species length differ"); + + return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, + this, species, + (m, s) -> s.maskFactory(m.toArray()).check(s)); + } + + @Override + @ForceInline + /*package-private*/ + Float16MaskMax indexPartiallyInUpperRange(long offset, long limit) { + return (Float16MaskMax) VectorSupport.indexPartiallyInUpperRange( + Float16MaskMax.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, + (o, l) -> (Float16MaskMax) TRUE_MASK.indexPartiallyInRange(o, l)); + } + + // Unary operations + + @Override + @ForceInline + public Float16MaskMax not() { + return xor(maskAll(true)); + } + + @Override + @ForceInline + public Float16MaskMax compress() { + return (Float16MaskMax)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, + Float16VectorMax.class, Float16MaskMax.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, + (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, Float16.float16ToRawShortBits(Float16.valueOf(m1.trueCount())))); + } + + + // Binary operations + + @Override + @ForceInline + public Float16MaskMax and(VectorMask mask) { + Objects.requireNonNull(mask); + Float16MaskMax m = (Float16MaskMax)mask; + return VectorSupport.binaryOp(VECTOR_OP_AND, Float16MaskMax.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); + } + + @Override + @ForceInline + public Float16MaskMax or(VectorMask mask) { + Objects.requireNonNull(mask); + Float16MaskMax m = (Float16MaskMax)mask; + return VectorSupport.binaryOp(VECTOR_OP_OR, Float16MaskMax.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); + } + + @Override + @ForceInline + public Float16MaskMax xor(VectorMask mask) { + Objects.requireNonNull(mask); + Float16MaskMax m = (Float16MaskMax)mask; + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float16MaskMax.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, m, null, + (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); + } + + // Mask Query operations + + @Override + @ForceInline + public int trueCount() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> trueCountHelper(m.getBits())); + } + + @Override + @ForceInline + public int firstTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> firstTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public int lastTrue() { + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> lastTrueHelper(m.getBits())); + } + + @Override + @ForceInline + public long toLong() { + if (length() > Long.SIZE) { + throw new UnsupportedOperationException("too many lanes for one long"); + } + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, + (m) -> toLongHelper(m.getBits())); + } + + // laneIsSet + + @Override + @ForceInline + public boolean laneIsSet(int i) { + Objects.checkIndex(i, length()); + return VectorSupport.extract(Float16MaskMax.class, LANE_TYPE_ORDINAL, VLENGTH, + this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; + } + + // Reductions + + @Override + @ForceInline + public boolean anyTrue() { + return VectorSupport.test(BT_ne, Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((Float16MaskMax)m).getBits())); + } + + @Override + @ForceInline + public boolean allTrue() { + return VectorSupport.test(BT_overflow, Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((Float16MaskMax)m).getBits())); + } + + @ForceInline + /*package-private*/ + static Float16MaskMax maskAll(boolean bit) { + return VectorSupport.fromBitsCoerced(Float16MaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, + (bit ? -1 : 0), MODE_BROADCAST, null, + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + } + private static final Float16MaskMax TRUE_MASK = new Float16MaskMax(true); + private static final Float16MaskMax FALSE_MASK = new Float16MaskMax(false); + + } + + // Shuffle + @ValueBased + static final class Float16ShuffleMax extends AbstractShuffle { + static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM + + Float16ShuffleMax(short[] indices) { + super(indices); + assert(VLENGTH == indices.length); + assert(indicesInRange(indices)); + } + + Float16ShuffleMax(int[] indices, int i) { + this(prepare(indices, i)); + } + + Float16ShuffleMax(IntUnaryOperator fn) { + this(prepare(fn)); + } + + short[] indices() { + return (short[])getPayload(); + } + + @Override + @ForceInline + public Float16Species vspecies() { + return VSPECIES; + } + + static { + // There must be enough bits in the shuffle lanes to encode + // VLENGTH valid indexes and VLENGTH exceptional ones. + assert(VLENGTH < Short.MAX_VALUE); + assert(Short.MIN_VALUE <= -VLENGTH); + } + static final Float16ShuffleMax IOTA = new Float16ShuffleMax(IDENTITY); + + @Override + @ForceInline + public Float16VectorMax toVector() { + return (Float16VectorMax) toBitsVector().castShape(VSPECIES, 0); + } + + @Override + @ForceInline + ShortVectorMax toBitsVector() { + return (ShortVectorMax) super.toBitsVectorTemplate(); + } + + @Override + ShortVectorMax toBitsVector0() { + return ((ShortVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); + } + + @Override + @ForceInline + public int laneSource(int i) { + return (int)toBitsVector().lane(i); + } + + @Override + @ForceInline + public void intoArray(int[] a, int offset) { + VectorSpecies species = IntVector.SPECIES_MAX; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoArray(a, offset); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoArray(a, offset + species.length()); + } + + @Override + @ForceInline + public void intoMemorySegment(MemorySegment ms, long offset, ByteOrder bo) { + VectorSpecies species = IntVector.SPECIES_MAX; + Vector v = toBitsVector(); + v.convertShape(VectorOperators.S2I, species, 0) + .reinterpretAsInts() + .intoMemorySegment(ms, offset, bo); + v.convertShape(VectorOperators.S2I, species, 1) + .reinterpretAsInts() + .intoMemorySegment(ms, offset + species.vectorByteSize(), bo); + } + + @Override + @ForceInline + public final Float16MaskMax laneIsValid() { + return (Float16MaskMax) toBitsVector().compare(VectorOperators.GE, 0) + .cast(VSPECIES); + } + + @ForceInline + @Override + public final Float16ShuffleMax rearrange(VectorShuffle shuffle) { + Float16ShuffleMax concreteShuffle = (Float16ShuffleMax) shuffle; + return (Float16ShuffleMax) toBitsVector().rearrange(concreteShuffle.cast(ShortVector.SPECIES_MAX)) + .toShuffle(VSPECIES, false); + } + + @ForceInline + @Override + public final Float16ShuffleMax wrapIndexes() { + ShortVectorMax v = toBitsVector(); + if ((length() & (length() - 1)) == 0) { + v = (ShortVectorMax) v.lanewise(VectorOperators.AND, length() - 1); + } else { + v = (ShortVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), + v.compare(VectorOperators.LT, 0)); + } + return (Float16ShuffleMax) v.toShuffle(VSPECIES, false); + } + + private static short[] prepare(int[] indices, int offset) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = indices[offset + i]; + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static short[] prepare(IntUnaryOperator f) { + short[] a = new short[VLENGTH]; + for (int i = 0; i < VLENGTH; i++) { + int si = f.applyAsInt(i); + si = partiallyWrapIndex(si, VLENGTH); + a[i] = (short)si; + } + return a; + } + + private static boolean indicesInRange(short[] indices) { + int length = indices.length; + for (short si : indices) { + if (si >= (short)length || si < (short)(-length)) { + String msg = ("index "+si+"out of range ["+length+"] in "+ + java.util.Arrays.toString(indices)); + throw new AssertionError(msg); + } + } + return true; + } + } + + // ================================================ + + // Specialized low-level memory operations. + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset) { + return super.fromArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, VectorMask m, int offsetInRange) { + return super.fromArray0Template(Float16MaskMax.class, a, offset, (Float16MaskMax) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromArray0(short[] a, int offset, int[] indexMap, int mapOffset, VectorMask m) { + return super.fromArray0Template(Float16MaskMax.class, a, offset, indexMap, mapOffset, (Float16MaskMax) m); + } + + + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset) { + return super.fromMemorySegment0Template(ms, offset); // specialize + } + + @ForceInline + @Override + final + Float16Vector fromMemorySegment0(MemorySegment ms, long offset, VectorMask m, int offsetInRange) { + return super.fromMemorySegment0Template(Float16MaskMax.class, ms, offset, (Float16MaskMax) m, offsetInRange); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset) { + super.intoArray0Template(a, offset); // specialize + } + + @ForceInline + @Override + final + void intoArray0(short[] a, int offset, VectorMask m) { + super.intoArray0Template(Float16MaskMax.class, a, offset, (Float16MaskMax) m); + } + + + + @ForceInline + @Override + final + void intoMemorySegment0(MemorySegment ms, long offset, VectorMask m) { + super.intoMemorySegment0Template(Float16MaskMax.class, ms, offset, (Float16MaskMax) m); + } + + + // End of specialized low-level memory operations. + + // ================================================ + +} + diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java index c18bbce1f34..69252017827 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java @@ -40,7 +40,8 @@ enum LaneType { BYTE(byte.class, Byte.class, byte[].class, 'I', -1, Byte.SIZE, byte.class), SHORT(short.class, Short.class, short[].class, 'I', -1, Short.SIZE, short.class), INT(int.class, Integer.class, int[].class, 'I', -1, Integer.SIZE, int.class), - LONG(long.class, Long.class, long[].class, 'I', -1, Long.SIZE, long.class); + LONG(long.class, Long.class, long[].class, 'I', -1, Long.SIZE, long.class), + FLOAT16(Float16.class, Short.class, short[].class, 'F', 11, Float16.SIZE, short.class); LaneType(Class elementType, Class genericElementType, @@ -66,7 +67,7 @@ enum LaneType { // printName. If we do unsigned or vector or bit lane types, // report that condition also. this.typeChar = genericElementType.getSimpleName().charAt(0); - assert("FDBSIL".indexOf(typeChar) == ordinal()) : this; + assert("FDBSILS".charAt(ordinal()) == typeChar) : this; this.carrierType = carrierType; assert(carrierType.isPrimitive()); @@ -181,7 +182,8 @@ enum LaneType { SK_SHORT = 4, SK_INT = 5, SK_LONG = 6, - SK_LIMIT = 7; + SK_FLOAT16 = 7, + SK_LIMIT = 8; /*package-private*/ @ForceInline @@ -278,5 +280,6 @@ enum LaneType { assert(ofLaneTypeOrdinal(LT_SHORT) == SHORT); assert(ofLaneTypeOrdinal(LT_INT) == INT); assert(ofLaneTypeOrdinal(LT_LONG) == LONG); + assert(ofLaneTypeOrdinal(LT_FLOAT16) == FLOAT16); } } 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 a9184d1faa9..ac381330e31 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 @@ -4149,22 +4149,14 @@ public abstract sealed class ShortVector extends AbstractVector /** * {@inheritDoc} - * - * @implNote This method always throws - * {@code UnsupportedOperationException}, because there is no floating - * point type of the same size as {@code short}. The return type - * of this method is arbitrarily designated as - * {@code Vector}. Future versions of this API may change the return - * type if additional floating point types become available. */ @ForceInline @Override public final - Vector + Float16Vector viewAsFloatingLanes() { LaneType flt = LaneType.SHORT.asFloating(); - // asFloating() will throw UnsupportedOperationException for the unsupported type short - throw new AssertionError("Cannot reach here"); + return (Float16Vector) asVectorRaw(flt); } // ================================================ 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 4e064e0af37..1bffb8d2238 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 @@ -200,11 +200,11 @@ import java.util.Arrays; * element type (such as access to element values in lanes, logical operations * on values of integral elements types, or transcendental operations on values * of floating point element types). - * There are six abstract subclasses of Vector corresponding to the supported set + * There are seven abstract subclasses of Vector corresponding to the supported set * of element types, {@link ByteVector}, {@link ShortVector}, - * {@link IntVector}, {@link LongVector}, {@link FloatVector}, and - * {@link DoubleVector}. Along with type-specific operations these classes - * support creation of vector values (instances of Vector). + * {@link IntVector}, {@link LongVector}, {@link FloatVector}, + * {@link DoubleVector}, and {@link Float16Vector}. Along with type-specific + * operations these classes support creation of vector values (instances of Vector). * They expose static constants corresponding to the supported species, * and static methods on these types generally take a species as a parameter. * For example, @@ -3826,6 +3826,19 @@ public abstract sealed class Vector extends jdk.internal.vm.vector.VectorSupp */ public abstract LongVector reinterpretAsLongs(); + /** + * Reinterprets this vector as a vector of the same shape + * and contents but a lane type of {@code Float16}, + * where the lanes are assembled from successive bytes + * according to little-endian order. + * It is a convenience method for the expression + * {@code reinterpretShape(species().withLanes(Float16.class))}. + * It may be considered an inverse to {@link Vector#reinterpretAsBytes()}. + * + * @return a {@code Float16Vector} with the same shape and information content + */ + public abstract Float16Vector reinterpretAsFloat16s(); + /** * Reinterprets this vector as a vector of the same shape * and contents but a lane type of {@code float}, 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 cc5a7ccbdef..7c6b6e69086 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 @@ -64,8 +64,9 @@ import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; * *

  • {@code bits(x)} — a function call which produces the * underlying bits of the value {@code x}. If {@code x} is a floating - * point value, this is either {@code doubleToLongBits(x)} or - * {@code floatToIntBits(x)}. Otherwise, the value is just {@code x}. + * point value, this is {@code doubleToLongBits(x)}, + * {@code floatToIntBits(x)}, or {@code float16ToShortBits(x)}. + * Otherwise, the value is just {@code x}. * *
  • {@code ESIZE} — the size in bytes of the operand type * @@ -73,6 +74,26 @@ import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; * *
  • {@code intVal}, {@code byteVal}, etc. — the operand of a * conversion, with the indicated type + * + *
  • Single-letter type codes used in the names of + * {@linkplain Conversion conversion} operator tokens (for example + * {@link #B2D}, {@link #F2H}, {@link #H2F}, {@link #REINTERPRET_F2I}, + * {@link #ZERO_EXTEND_B2L}) abbreviate lane types as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Lane type letter codes
    LetterLane type
    {@code B}{@code byte}
    {@code S}{@code short}
    {@code I}{@code int}
    {@code L}{@code long}
    {@code F}{@code float}
    {@code D}{@code double}
    {@code H}{@link Float16} ("half")
    * * *

    Operations on floating point vectors

    @@ -307,13 +328,13 @@ public final class VectorOperators { */ public sealed interface Conversion extends Operator { /** - * The domain of this conversion, a primitive type. + * The domain of this conversion, a supported lane type. * @return the domain of this conversion */ Class domainType(); /** - * The range of this conversion, a primitive type. + * The range of this conversion, a supported lane type. * @return the range of this conversion */ @Override @@ -657,6 +678,8 @@ public final class VectorOperators { public static final Conversion B2L = convert("B2L", 'C', byte.class, long.class, VO_KIND_CAST, VO_ALL); /** Convert {@code byteVal} to {@code (short)byteVal}. */ public static final Conversion B2S = convert("B2S", 'C', byte.class, short.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code byteVal} to {@code (Float16)byteVal}. */ + public static final Conversion B2H = convert("B2H", 'C', byte.class, Float16.class, VO_KIND_CAST, VO_ALL); /** Convert {@code doubleVal} to {@code (byte)doubleVal}. */ public static final Conversion D2B = convert("D2B", 'C', double.class, byte.class, VO_KIND_CAST, VO_ALL); /** Convert {@code doubleVal} to {@code (float)doubleVal}. */ @@ -667,6 +690,8 @@ public final class VectorOperators { public static final Conversion D2L = convert("D2L", 'C', double.class, long.class, VO_KIND_CAST, VO_ALL); /** Convert {@code doubleVal} to {@code (short)doubleVal}. */ public static final Conversion D2S = convert("D2S", 'C', double.class, short.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code doubleVal} to {@code (Float16)doubleVal}. */ + public static final Conversion D2H = convert("D2H", 'C', double.class, Float16.class, VO_KIND_CAST, VO_ALL); /** Convert {@code floatVal} to {@code (byte)floatVal}. */ public static final Conversion F2B = convert("F2B", 'C', float.class, byte.class, VO_KIND_CAST, VO_ALL); /** Convert {@code floatVal} to {@code (double)floatVal}. */ @@ -677,6 +702,8 @@ public final class VectorOperators { public static final Conversion F2L = convert("F2L", 'C', float.class, long.class, VO_KIND_CAST, VO_ALL); /** Convert {@code floatVal} to {@code (short)floatVal}. */ public static final Conversion F2S = convert("F2S", 'C', float.class, short.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code floatVal} to {@code (Float16)floatVal}. */ + public static final Conversion F2H = convert("F2H", 'C', float.class, Float16.class, VO_KIND_CAST, VO_ALL); /** Convert {@code intVal} to {@code (byte)intVal}. */ public static final Conversion I2B = convert("I2B", 'C', int.class, byte.class, VO_KIND_CAST, VO_ALL); /** Convert {@code intVal} to {@code (double)intVal}. */ @@ -687,6 +714,8 @@ public final class VectorOperators { public static final Conversion I2L = convert("I2L", 'C', int.class, long.class, VO_KIND_CAST, VO_ALL); /** Convert {@code intVal} to {@code (short)intVal}. */ public static final Conversion I2S = convert("I2S", 'C', int.class, short.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code intVal} to {@code (Float16)intVal}. */ + public static final Conversion I2H = convert("I2H", 'C', int.class, Float16.class, VO_KIND_CAST, VO_ALL); /** Convert {@code longVal} to {@code (byte)longVal}. */ public static final Conversion L2B = convert("L2B", 'C', long.class, byte.class, VO_KIND_CAST, VO_ALL); /** Convert {@code longVal} to {@code (double)longVal}. */ @@ -697,6 +726,8 @@ public final class VectorOperators { public static final Conversion L2I = convert("L2I", 'C', long.class, int.class, VO_KIND_CAST, VO_ALL); /** Convert {@code longVal} to {@code (short)longVal}. */ public static final Conversion L2S = convert("L2S", 'C', long.class, short.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code longVal} to {@code (Float16)longVal}. */ + public static final Conversion L2H = convert("L2H", 'C', long.class, Float16.class, VO_KIND_CAST, VO_ALL); /** Convert {@code shortVal} to {@code (byte)shortVal}. */ public static final Conversion S2B = convert("S2B", 'C', short.class, byte.class, VO_KIND_CAST, VO_ALL); /** Convert {@code shortVal} to {@code (double)shortVal}. */ @@ -707,6 +738,21 @@ public final class VectorOperators { public static final Conversion S2I = convert("S2I", 'C', short.class, int.class, VO_KIND_CAST, VO_ALL); /** Convert {@code shortVal} to {@code (long)shortVal}. */ public static final Conversion S2L = convert("S2L", 'C', short.class, long.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code shortVal} to {@code (Float16)shortVal}. */ + public static final Conversion S2H = convert("S2H", 'C', short.class, Float16.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code Float16Val} to {@code (byte)Float16Val}. */ + public static final Conversion H2B = convert("H2B", 'C', Float16.class, byte.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code Float16Val} to {@code (short)Float16Val}. */ + public static final Conversion H2S = convert("H2S", 'C', Float16.class, short.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code Float16Val} to {@code (double)Float16Val}. */ + public static final Conversion H2D = convert("H2D", 'C', Float16.class, double.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code Float16Val} to {@code (float)Float16Val}. */ + public static final Conversion H2F = convert("H2F", 'C', Float16.class, float.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code Float16Val} to {@code (int)Float16Val}. */ + public static final Conversion H2I = convert("H2I", 'C', Float16.class, int.class, VO_KIND_CAST, VO_ALL); + /** Convert {@code Float16Val} to {@code (long)Float16Val}. */ + public static final Conversion H2L = convert("H2L", 'C', Float16.class, long.class, VO_KIND_CAST, VO_ALL); + /** Reinterpret bits of {@code doubleVal} as {@code long}. As if by {@link Double#doubleToRawLongBits(double)} */ public static final Conversion REINTERPRET_D2L = convert("REINTERPRET_D2L", 'R', double.class, long.class, VO_KIND_BITWISE, VO_ALL); /** Reinterpret bits of {@code floatVal} as {@code int}. As if by {@link Float#floatToRawIntBits(float)} */ 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 74d6ce45db0..00445cc8ac5 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 @@ -41,12 +41,65 @@ import static jdk.internal.vm.vector.VectorSupport.*; import static jdk.incubator.vector.VectorIntrinsics.*; import static jdk.incubator.vector.VectorOperators.*; +#if[FP16] +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import static java.lang.Float.*; +#end[FP16] #warn This file is preprocessed before being compiled /** * A specialized {@link Vector} representing an ordered immutable sequence of +#if[FP16] + * 16-bit data values in the IEEE 754 binary16 format. + *

    + * The scalar {@linkplain Float16Vector#elementType() element type} of {@code Float16Vector} + * is the class {@link Float16}, a value-based + * class holding 16-bit data in IEEE 754 binary16 format. However, the {@code Float16} + * class is not used by vector operations that accept scalar element values, or + * arrays of scalar element values. Instead, the primitive type {@code short} is + * used to explicitly hold 16-bit data in IEEE 754 binary16 format. For such operations + * it may be necessary to explicitly convert between floating-point values of {@code Float16} + * or {@code float} and values of {@code short} using the appropriate conversion + * methods on {@code Float16} or {@code Float}. + * + *

    + * The specifications for operations on elements of this class are written as if + * {@code Float16} is a primitive floating-point type. An operation referencing a + * Java operator is mapped to a method on {@code Float16} that specifies that + * operator's semantics. For example, the semantics of the {@code +} operator, + * as referenced by {@link Vector#add(Vector)} and {@link VectorOperators#ADD}, + * is mapped to the method {@link Float16#add(Float16, Float16)}. + * An operation referencing a method on {@link Math} is mapped to a method of the + * same name on {@code Float16}, if it exists. For example, {@link Math#fma} is + * mapped to {@link Float16#fma}, as referenced by {@link Float16Vector#fma(short, short)} + * and {@link VectorOperators#FMA}. + * Otherwise, if there is no equivalent method on {@code Float16}, the expression that is + * an invocation of a method on {@code Math} is mapped to an expression that converts + * the {@code Float16} arguments to {@code double} values or {@code float} values as + * required by the method's parameter types, invokes the method on {@code Math} with + * the converted values, and converts the resulting {@code double} or {@code float} value + * to a {@code Float16} value. For example, {@link Math#sin} is mapped to the expression + * {@code Float16.valueOf(Math.sin(a.doubleValue()))}, where {@code a} is the + * {@code Float16} lane value, as referenced by {@link VectorOperators#SIN}. + * + * @apiNote + * {@code Float16} is currently a value-based class and therefore cannot be optimally + * used as the scalar element type of vector operations until it becomes a value class + * that behaves similarly to the primitive type {@code short} and to arrays of {@code short}. + * For example, accessing {@code Float16} vectors using arrays requires those arrays be + * {@code short[]} arrays. Accessing vectors using memory segments requires, naturally, + * that consecutive 16-bits of memory hold 16-bit data values in the IEEE 754 binary16 + * format. + * @see Float16 + * @see Float16#float16ToRawShortBits(Float16) + * @see Float16#shortBitsToFloat16(short) + * @see Float#floatToFloat16(float) + * @see Float#float16ToFloat(short) +#else[FP16] * {@code $type$} values. +#end[FP16] */ @SuppressWarnings("cast") // warning: redundant cast public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtype$> @@ -62,7 +115,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp static final int FORBID_OPCODE_KIND = VO_ONLYFP; #end[FP] - static final ValueLayout.Of$Type$ ELEMENT_LAYOUT = ValueLayout.JAVA_$TYPE$.withByteAlignment(1); + static final ValueLayout.Of$ElemLayout$ ELEMENT_LAYOUT = ValueLayout.JAVA_$TYPE$.withByteAlignment(1); static final int LANE_TYPE_ORDINAL = $laneType$; @@ -158,7 +211,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /*package-private*/ interface FUnOp { - $type$ apply(int i, $type$ a); + $fallbacktype$ apply(int i, $fallbacktype$ a); } /*package-private*/ @@ -170,7 +223,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] vec = vec(); $type$[] res = new $type$[length()]; for (int i = 0; i < res.length; i++) { +#if[FP16] + res[i] = floatToFloat16(f.apply(i, float16ToFloat(vec[i]))); +#else[FP16] res[i] = f.apply(i, vec[i]); +#end[FP16] } return vectorFactory(res); } @@ -190,16 +247,60 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] res = new $type$[length()]; boolean[] mbits = ((AbstractMask<$Boxtype$>)m).getBits(); for (int i = 0; i < res.length; i++) { +#if[FP16] + res[i] = mbits[i] ? floatToFloat16(f.apply(i, float16ToFloat(vec[i]))) : vec[i]; +#else[FP16] res[i] = mbits[i] ? f.apply(i, vec[i]) : vec[i]; +#end[FP16] } return vectorFactory(res); } +#if[FP16] + /*package-private*/ + interface FUnRawOp { + $type$ apply(int i, $type$ a); + } + + /*package-private*/ + abstract + $abstractvectortype$ uRawOp(FUnRawOp f); + @ForceInline + final + $abstractvectortype$ uRawOpTemplate(FUnRawOp f) { + $type$[] vec = vec(); + $type$[] res = new $type$[length()]; + for (int i = 0; i < res.length; i++) { + res[i] = f.apply(i, vec[i]); + } + return vectorFactory(res); + } + + /*package-private*/ + abstract + $abstractvectortype$ uRawOp(VectorMask<$Boxtype$> m, + FUnRawOp f); + @ForceInline + final + $abstractvectortype$ uRawOpTemplate(VectorMask<$Boxtype$> m, + FUnRawOp f) { + if (m == null) { + return uRawOpTemplate(f); + } + $type$[] vec = vec(); + $type$[] res = new $type$[length()]; + boolean[] mbits = ((AbstractMask<$Boxtype$>)m).getBits(); + for (int i = 0; i < res.length; i++) { + res[i] = mbits[i] ? f.apply(i, vec[i]) : vec[i]; + } + return vectorFactory(res); + } +#end[FP16] // Binary operator /*package-private*/ interface FBinOp { - $type$ apply(int i, $type$ a, $type$ b); + $fallbacktype$ apply(int i, $fallbacktype$ a, $fallbacktype$ b); } /*package-private*/ @@ -214,7 +315,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] vec1 = this.vec(); $type$[] vec2 = (($abstractvectortype$)o).vec(); for (int i = 0; i < res.length; i++) { +#if[FP16] + res[i] = floatToFloat16(f.apply(i, float16ToFloat(vec1[i]), float16ToFloat(vec2[i]))); +#else[FP16] res[i] = f.apply(i, vec1[i], vec2[i]); +#end[FP16] } return vectorFactory(res); } @@ -237,7 +342,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] vec2 = (($abstractvectortype$)o).vec(); boolean[] mbits = ((AbstractMask<$Boxtype$>)m).getBits(); for (int i = 0; i < res.length; i++) { +#if[FP16] + res[i] = mbits[i] ? floatToFloat16(f.apply(i, float16ToFloat(vec1[i]), float16ToFloat(vec2[i]))) : vec1[i]; +#else[FP16] res[i] = mbits[i] ? f.apply(i, vec1[i], vec2[i]) : vec1[i]; +#end[FP16] } return vectorFactory(res); } @@ -310,7 +419,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] vec = vec(); boolean[] mbits = ((AbstractMask<$Boxtype$>)m).getBits(); for (int i = 0; i < vec.length; i++) { +#if[FP16] + v = mbits[i] ? floatToFloat16(f.apply(i, float16ToFloat(v), float16ToFloat(vec[i]))) : v; +#else[FP16] v = mbits[i] ? f.apply(i, v, vec[i]) : v; +#end[FP16] } return v; } @@ -320,7 +433,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$ rOpTemplate($type$ v, FBinOp f) { $type$[] vec = vec(); for (int i = 0; i < vec.length; i++) { +#if[FP16] + v = floatToFloat16(f.apply(i, float16ToFloat(v), float16ToFloat(vec[i]))); +#else[FP16] v = f.apply(i, v, vec[i]); +#end[FP16] } return v; } @@ -516,13 +633,21 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /*package-private*/ @ForceInline static long toBits($type$ e) { +#if[FP16] + return e; +#else[FP16] return {#if[FP]? $Type$.$type$ToRaw$Bitstype$Bits(e): e}; +#end[FP16] } /*package-private*/ @ForceInline static $type$ fromBits(long bits) { +#if[FP16] + return (short)bits; +#else[FP16] return {#if[FP]?$Type$.$bitstype$BitsTo$Type$}(($bitstype$)bits); +#end[FP16] } static $abstractvectortype$ expandHelper(Vector<$Boxtype$> v, VectorMask<$Boxtype$> m) { @@ -562,7 +687,12 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] vecPayload2 = (($abstractvectortype$)src1).vec(); $type$[] vecPayload3 = (($abstractvectortype$)src2).vec(); for (int i = 0; i < vlen; i++) { +#if[FP16] + int index = shortBitsToFloat16(vecPayload1[i]).intValue(); + int wrapped_index = VectorIntrinsics.wrapToRange(index, 2 * vlen); +#else[FP16] int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); +#end[FP16] res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; } return (($abstractvectortype$)src1).vectorFactory(res); @@ -594,7 +724,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $Type$Species vsp = ($Type$Species) species; #if[FP] return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), - toBits(0.0f), MODE_BROADCAST, vsp, + toBits({#if[FP16]?(short) 0:0.0f}), MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); #else[FP] return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), @@ -784,27 +914,32 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp private static UnaryOperation<$abstractvectortype$, VectorMask<$Boxtype$>> unaryOperations(int opc_) { switch (opc_) { +#if[FP16] case VECTOR_OP_NEG: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) -a); + v0.uOp(m, (i, a) -> Float16.negate(Float16.valueOf(a)).floatValue()); +#else[FP16] + case VECTOR_OP_NEG: return (v0, m) -> + v0.uOp(m, (i, a) -> ($fallbacktype$) -a); +#end[FP16] case VECTOR_OP_ABS: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.abs(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.abs(a)); #if[!FP] #if[intOrLong] case VECTOR_OP_BIT_COUNT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) $Boxtype$.bitCount(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) $Boxtype$.bitCount(a)); case VECTOR_OP_TZ_COUNT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) $Boxtype$.numberOfTrailingZeros(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) $Boxtype$.numberOfTrailingZeros(a)); case VECTOR_OP_LZ_COUNT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) $Boxtype$.numberOfLeadingZeros(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) $Boxtype$.numberOfLeadingZeros(a)); case VECTOR_OP_REVERSE: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) $Boxtype$.reverse(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) $Boxtype$.reverse(a)); #else[intOrLong] case VECTOR_OP_BIT_COUNT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) bitCount(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) bitCount(a)); case VECTOR_OP_TZ_COUNT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) numberOfTrailingZeros(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) numberOfTrailingZeros(a)); case VECTOR_OP_LZ_COUNT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) numberOfLeadingZeros(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) numberOfLeadingZeros(a)); case VECTOR_OP_REVERSE: return (v0, m) -> v0.uOp(m, (i, a) -> reverse(a)); #end[intOrLong] @@ -814,43 +949,47 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp v0.uOp(m, (i, a) -> a); #else[byte] case VECTOR_OP_REVERSE_BYTES: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) $Boxtype$.reverseBytes(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) $Boxtype$.reverseBytes(a)); #end[byte] #end[BITWISE] #end[!FP] #if[FP] case VECTOR_OP_SIN: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.sin(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.sin(a)); case VECTOR_OP_COS: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.cos(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.cos(a)); case VECTOR_OP_TAN: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.tan(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.tan(a)); case VECTOR_OP_ASIN: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.asin(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.asin(a)); case VECTOR_OP_ACOS: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.acos(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.acos(a)); case VECTOR_OP_ATAN: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.atan(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.atan(a)); case VECTOR_OP_EXP: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.exp(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.exp(a)); case VECTOR_OP_LOG: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.log(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.log(a)); case VECTOR_OP_LOG10: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.log10(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.log10(a)); case VECTOR_OP_SQRT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.sqrt(a)); +#if[FP16] + v0.uOp(m, (i, a) -> Float16.sqrt(Float16.valueOf(a)).floatValue()); +#else[FP16] + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.sqrt(a)); +#end[FP16] case VECTOR_OP_CBRT: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.cbrt(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.cbrt(a)); case VECTOR_OP_SINH: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.sinh(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.sinh(a)); case VECTOR_OP_COSH: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.cosh(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.cosh(a)); case VECTOR_OP_TANH: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.tanh(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.tanh(a)); case VECTOR_OP_EXPM1: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.expm1(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.expm1(a)); case VECTOR_OP_LOG1P: return (v0, m) -> - v0.uOp(m, (i, a) -> ($type$) Math.log1p(a)); + v0.uOp(m, (i, a) -> ($fallbacktype$) Math.log1p(a)); #end[FP] default: return null; } @@ -996,46 +1135,46 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp private static BinaryOperation<$abstractvectortype$, VectorMask<$Boxtype$>> binaryOperations(int opc_) { switch (opc_) { case VECTOR_OP_ADD: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a + b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a + b)); case VECTOR_OP_SUB: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a - b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a - b)); case VECTOR_OP_MUL: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a * b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a * b)); case VECTOR_OP_DIV: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a / b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a / b)); case VECTOR_OP_MAX: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)Math.max(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)Math.max(a, b)); case VECTOR_OP_MIN: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)Math.min(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)Math.min(a, b)); #if[BITWISE] case VECTOR_OP_AND: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a & b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a & b)); case VECTOR_OP_OR: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a | b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a | b)); case VECTOR_OP_XOR: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(a ^ b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(a ^ b)); case VECTOR_OP_LSHIFT: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, n) -> ($type$)(a << n)); + v0.bOp(v1, vm, (i, a, n) -> ($fallbacktype$)(a << n)); case VECTOR_OP_RSHIFT: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, n) -> ($type$)(a >> n)); + v0.bOp(v1, vm, (i, a, n) -> ($fallbacktype$)(a >> n)); case VECTOR_OP_URSHIFT: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, n) -> ($type$)((a & LSHR_SETUP_MASK) >>> n)); + v0.bOp(v1, vm, (i, a, n) -> ($fallbacktype$)((a & LSHR_SETUP_MASK) >>> n)); case VECTOR_OP_LROTATE: return (v0, v1, vm) -> v0.bOp(v1, vm, (i, a, n) -> rotateLeft(a, (int)n)); case VECTOR_OP_RROTATE: return (v0, v1, vm) -> v0.bOp(v1, vm, (i, a, n) -> rotateRight(a, (int)n)); case VECTOR_OP_UMAX: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)VectorMath.maxUnsigned(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)VectorMath.maxUnsigned(a, b)); case VECTOR_OP_UMIN: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)VectorMath.minUnsigned(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)VectorMath.minUnsigned(a, b)); case VECTOR_OP_SADD: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(VectorMath.addSaturating(a, b))); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(VectorMath.addSaturating(a, b))); case VECTOR_OP_SSUB: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(VectorMath.subSaturating(a, b))); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(VectorMath.subSaturating(a, b))); case VECTOR_OP_SUADD: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(VectorMath.addSaturatingUnsigned(a, b))); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_SUSUB: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$)(VectorMath.subSaturatingUnsigned(a, b))); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$)(VectorMath.subSaturatingUnsigned(a, b))); #if[intOrLong] case VECTOR_OP_COMPRESS_BITS: return (v0, v1, vm) -> v0.bOp(v1, vm, (i, a, n) -> $Boxtype$.compress(a, n)); @@ -1045,13 +1184,17 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp #end[BITWISE] #if[FP] case VECTOR_OP_OR: return (v0, v1, vm) -> +#if[FP16] + v0.bOp(v1, vm, (i, a, b) -> FloatVector.fromBits(FloatVector.toBits(a) | FloatVector.toBits(b))); +#else[FP16] v0.bOp(v1, vm, (i, a, b) -> fromBits(toBits(a) | toBits(b))); +#end[FP16] case VECTOR_OP_ATAN2: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$) Math.atan2(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$) Math.atan2(a, b)); case VECTOR_OP_POW: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$) Math.pow(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$) Math.pow(a, b)); case VECTOR_OP_HYPOT: return (v0, v1, vm) -> - v0.bOp(v1, vm, (i, a, b) -> ($type$) Math.hypot(a, b)); + v0.bOp(v1, vm, (i, a, b) -> ($fallbacktype$) Math.hypot(a, b)); #end[FP] default: return null; } @@ -1147,13 +1290,13 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp public final $abstractvectortype$ lanewise(VectorOperators.Binary op, long e) { - $type$ e1 = ($type$) e; + $type$ e1 = {#if[FP16]?float16ToRawShortBits(Float16.valueOf(e)):($type$) e}; #if[BITWISE] if ((long)e1 != e // allow shift ops to clip down their int parameters && !(opKind(op, VO_SHIFT) && (int)e1 == e)) { #else[BITWISE] - if ((long)e1 != e) { + if ({#if[FP16]?shortBitsToFloat16(e1).longValue():(long)e1} != e) { #end[BITWISE] vspecies().checkValue(e); // for exception } @@ -1174,13 +1317,13 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp public final $abstractvectortype$ lanewise(VectorOperators.Binary op, long e, VectorMask<$Boxtype$> m) { - $type$ e1 = ($type$) e; + $type$ e1 = {#if[FP16]?float16ToRawShortBits(Float16.valueOf(e)):($type$) e}; #if[BITWISE] if ((long)e1 != e // allow shift ops to clip down their int parameters && !(opKind(op, VO_SHIFT) && (int)e1 == e)) { #else[BITWISE] - if ((long)e1 != e) { + if ({#if[FP16]?shortBitsToFloat16(e1).longValue():(long)e1} != e) { #end[BITWISE] vspecies().checkValue(e); // for exception } @@ -1255,12 +1398,12 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp // since our lane types are first-class types, not just dressed // up ints. private static final int SHIFT_MASK = ($Boxtype$.SIZE - 1); -#if[byteOrShort] +#if[byteOrStrictShort] // Also simulate >>> on sub-word variables with a mask. private static final int LSHR_SETUP_MASK = ((1 << $Boxtype$.SIZE) - 1); -#else[byteOrShort] +#else[byteOrStrictShort] private static final $type$ LSHR_SETUP_MASK = -1; -#end[byteOrShort] +#end[byteOrStrictShort] #end[BITWISE] // Ternary lanewise support @@ -1363,7 +1506,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp switch (opc_) { #if[FP] case VECTOR_OP_FMA: return (v0, v1_, v2_, m) -> +#if[FP16] + v0.tOp(v1_, v2_, m, (i, a, b, c) -> float16ToRawShortBits(Float16.fma(shortBitsToFloat16(a), shortBitsToFloat16(b), shortBitsToFloat16(c)))); +#else[FP16] v0.tOp(v1_, v2_, m, (i, a, b, c) -> Math.fma(a, b, c)); +#end[FP16] #end[FP] default: return null; } @@ -2392,7 +2539,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp // first kill the sign: bits = bits.and($Boxbitstype$.MAX_VALUE); // next find the bit pattern for infinity: - $bitstype$ infbits = ($bitstype$) toBits($Boxtype$.POSITIVE_INFINITY); + $bitstype$ infbits = ($bitstype$) toBits({#if[FP16]?float16ToRawShortBits($Boxtype$.POSITIVE_INFINITY):$Boxtype$.POSITIVE_INFINITY}); // now compare: if (op == IS_FINITE) { m = bits.compare(LT, infbits); @@ -2446,7 +2593,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp // first kill the sign: bits = bits.and($Boxbitstype$.MAX_VALUE); // next find the bit pattern for infinity: - $bitstype$ infbits = ($bitstype$) toBits($Boxtype$.POSITIVE_INFINITY); + $bitstype$ infbits = ($bitstype$) toBits({#if[FP16]?float16ToRawShortBits($Boxtype$.POSITIVE_INFINITY):$Boxtype$.POSITIVE_INFINITY}); // now compare: if (op == IS_FINITE) { m = bits.compare(LT, infbits, m); @@ -2517,14 +2664,23 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } @ForceInline - private static boolean compareWithOp(int cond, $type$ a, $type$ b) { + private static boolean compareWithOp(int cond, $carriertype$ a, $carriertype$ b) { return switch (cond) { +#if[FP16] + case BT_eq -> Float.float16ToFloat(a) == Float.float16ToFloat(b); + case BT_ne -> Float.float16ToFloat(a) != Float.float16ToFloat(b); + case BT_lt -> Float.float16ToFloat(a) < Float.float16ToFloat(b); + case BT_le -> Float.float16ToFloat(a) <= Float.float16ToFloat(b); + case BT_gt -> Float.float16ToFloat(a) > Float.float16ToFloat(b); + case BT_ge -> Float.float16ToFloat(a) >= Float.float16ToFloat(b); +#else[FP16] case BT_eq -> a == b; case BT_ne -> a != b; case BT_lt -> a < b; case BT_le -> a <= b; case BT_gt -> a > b; case BT_ge -> a >= b; +#end[FP16] #if[!FP] case BT_ult -> $Boxtype$.compareUnsigned(a, b) < 0; case BT_ule -> $Boxtype$.compareUnsigned(a, b) <= 0; @@ -2665,7 +2821,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp // and multiply. $abstractvectortype$ iota = s.iota(); $type$ sc = ($type$) scale_; - return v.add(sc == 1 ? iota : iota.mul(sc)); + return v.add(sc == 1 ? iota : iota.mul({#if[FP16]?float16ToRawShortBits(Float16.valueOf(sc)):sc})); }); } @@ -2875,7 +3031,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return VectorSupport.rearrangeOp( getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, - (v1, s_, m_) -> v1.uOp((i, a) -> { + (v1, s_, m_) -> v1.{#if[FP16]?uRawOp:uOp}((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); return v1.lane(ei); })); @@ -2902,7 +3058,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, - (v1, s_, m_) -> v1.uOp((i, a) -> { + (v1, s_, m_) -> v1.{#if[FP16]?uRawOp:uOp}((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); @@ -2928,7 +3084,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp VectorSupport.rearrangeOp( getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, - (v0, s_, m_) -> v0.uOp((i, a) -> { + (v0, s_, m_) -> v0.{#if[FP16]?uRawOp:uOp}((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); return v0.lane(ei); })); @@ -2936,7 +3092,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp VectorSupport.rearrangeOp( getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, - (v1, s_, m_) -> v1.uOp((i, a) -> { + (v1, s_, m_) -> v1.{#if[FP16]?uRawOp:uOp}((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); return v1.lane(ei); })); @@ -2963,6 +3119,9 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp final VectorShuffle toShuffle(AbstractSpecies dsp, boolean wrap) { assert(dsp.elementSize() == vspecies().elementSize()); +#if[FP16] + ShortVector idx = convert(VectorOperators.H2S, 0).reinterpretAsShorts(); +#end[FP16] #if[float] IntVector idx = convert(VectorOperators.F2I, 0).reinterpretAsInts(); #end[float] @@ -2983,7 +3142,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * {@inheritDoc} - * @since 19 + * @since {#if[FP16]?27:19} */ @Override public abstract @@ -3002,7 +3161,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * {@inheritDoc} - * @since 19 + * @since {#if[FP16]?27:19} */ @Override public abstract @@ -3199,7 +3358,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * * This is a lane-wise ternary operation which applies an operation * conforming to the specification of - * {@link Math#fma($type$,$type$,$type$) Math.fma(a,b,c)} + * {@link Math#fma($fallbacktype$,$fallbacktype$,$fallbacktype$) Math.fma(a,b,c)} * to each lane. #if[intOrFloat] * The operation is adapted to cast the operands and the result, @@ -3240,7 +3399,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * * This is a lane-wise ternary operation which applies an operation * conforming to the specification of - * {@link Math#fma($type$,$type$,$type$) Math.fma(a,b,c)} + * {@link Math#fma($fallbacktype$,$fallbacktype$,$fallbacktype$) Math.fma(a,b,c)} * to each lane. #if[intOrFloat] * The operation is adapted to cast the operands and the result, @@ -3447,13 +3606,13 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp private static ReductionOperation<$abstractvectortype$, VectorMask<$Boxtype$>> reductionOperations(int opc_) { switch (opc_) { case VECTOR_OP_ADD: return (v, m) -> - toBits(v.rOp(($type$)0, m, (i, a, b) -> ($type$)(a + b))); + toBits(v.rOp(($type$)0, m, (i, a, b) -> ($fallbacktype$)(a + b))); case VECTOR_OP_MUL: return (v, m) -> - toBits(v.rOp(($type$)1, m, (i, a, b) -> ($type$)(a * b))); + toBits(v.rOp(($type$){#if[FP16]?floatToFloat16(1.0f):1}, m, (i, a, b) -> ($fallbacktype$)(a * b))); case VECTOR_OP_MIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> ($type$) Math.min(a, b))); + toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> ($fallbacktype$) Math.min(a, b))); case VECTOR_OP_MAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($type$) Math.max(a, b))); + toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($fallbacktype$) Math.max(a, b))); #if[!FP] case VECTOR_OP_UMIN: return (v, m) -> toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> ($type$) VectorMath.minUnsigned(a, b))); @@ -3475,8 +3634,8 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } #if[FP] - private static final $type$ MIN_OR_INF = $Boxtype$.NEGATIVE_INFINITY; - private static final $type$ MAX_OR_INF = $Boxtype$.POSITIVE_INFINITY; + private static final $type$ MIN_OR_INF = {#if[FP16]?float16ToRawShortBits($Boxtype$.NEGATIVE_INFINITY):$Boxtype$.NEGATIVE_INFINITY}; + private static final $type$ MAX_OR_INF = {#if[FP16]?float16ToRawShortBits($Boxtype$.POSITIVE_INFINITY):$Boxtype$.POSITIVE_INFINITY}; #else[FP] private static final $type$ MIN_OR_INF = $Boxtype$.MIN_VALUE; private static final $type$ MAX_OR_INF = $Boxtype$.MAX_VALUE; @@ -3546,6 +3705,19 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return a; } +#if[FP16] + // Returns the lane values boxed as Float16 elements. + @ForceInline + final Float16[] toFloat16Array() { + short[] bits = vec(); + Float16[] a = new Float16[bits.length]; + for (int i = 0; i < bits.length; i++) { + a[i] = Float16.shortBitsToFloat16(bits[i]); + } + return a; + } + +#end[FP16] #if[int] /** * {@inheritDoc} @@ -3656,14 +3828,18 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] a = toArray(); double[] res = new double[a.length]; for (int i = 0; i < a.length; i++) { - res[i] = (double) a[i]; + res[i] = (double) {#if[FP16]?shortBitsToFloat16(a[i]).doubleValue():a[i]}; } return res; } #end[double] /** +#if[FP16] + * Loads a vector from an array of type {@code $type$[]} holding IEEE 754 binary16 values +#else[FP16] * Loads a vector from an array of type {@code $type$[]} +#end[FP16] * starting at an offset. * For each vector lane, where {@code N} is the vector lane index, the * array element at index {@code offset + N} is placed into the @@ -3687,7 +3863,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } /** +#if[FP16] + * Loads a vector from an array of type {@code $type$[]} holding IEEE 754 binary16 values +#else[FP16] * Loads a vector from an array of type {@code $type$[]} +#end[FP16] * starting at an offset and using a mask. * Lanes where the mask is unset are filled with the default * value of {@code $type$} ({#if[FP]?positive }zero). @@ -3724,7 +3904,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * Gathers a new vector composed of elements from an array of type +#if[FP16] + * {@code $type$[]} holding IEEE 754 binary16 values, +#else[FP16] * {@code $type$[]}, +#end[FP16] * using indexes obtained by adding a fixed {@code offset} to a * series of secondary offsets from an index map. * The index map is a contiguous sequence of {@code VLENGTH} @@ -3869,7 +4053,11 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * Gathers a new vector composed of elements from an array of type +#if[FP16] + * {@code $type$[]} holding IEEE 754 binary16 values, +#else[FP16] * {@code $type$[]}, +#end[FP16] * under the control of a mask, and * using indexes obtained by adding a fixed {@code offset} to a * series of secondary offsets from an index map. @@ -3918,7 +4106,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } } -#if[short] +#if[strictShort] /** * Loads a vector from an array of type {@code char[]} * starting at an offset. @@ -4069,7 +4257,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $Type$Species vsp = ($Type$Species) species; return vsp.vOp(m, n -> (short) a[offset + indexMap[mapOffset + n]]); } -#end[short] +#end[strictShort] #if[byte] /** @@ -4258,7 +4446,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * for any lane {@code N} in the vector * @throws IllegalStateException if the memory segment's session is not alive, * or if access occurs from a thread other than the thread owning the session. - * @since 19 + * @since {#if[FP16]?27:19} */ @ForceInline public static @@ -4317,7 +4505,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * where the mask is set * @throws IllegalStateException if the memory segment's session is not alive, * or if access occurs from a thread other than the thread owning the session. - * @since 19 + * @since {#if[FP16]?27:19} */ @ForceInline public static @@ -4555,7 +4743,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } #end[byteOrShort] -#if[short] +#if[strictShort] /** * Stores this vector into an array of type {@code char[]} * starting at an offset. @@ -4711,7 +4899,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp arr[off + j] = (char) e; }); } -#end[short] +#end[strictShort] #if[byte] /** @@ -4886,7 +5074,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * {@inheritDoc} - * @since 19 + * @since {#if[FP16]?27:19} */ @Override @ForceInline @@ -4903,7 +5091,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * {@inheritDoc} - * @since 19 + * @since {#if[FP16]?27:19} */ @Override @ForceInline @@ -5100,7 +5288,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } #end[byteOrShort] -#if[short] +#if[strictShort] /*package-private*/ abstract $abstractvectortype$ fromCharArray0(char[] a, int offset); @@ -5132,7 +5320,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, (arr_, off_, i) -> (short) arr_[off_ + i])); } -#end[short] +#end[strictShort] #if[byte] /*package-private*/ @@ -5346,7 +5534,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp }); } -#if[short] +#if[strictShort] /*package-private*/ abstract void intoCharArray0(char[] a, int offset, VectorMask<$Boxtype$> m); @@ -5364,7 +5552,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp -> v.stOp(arr, (int) off, vm, (arr_, off_, i, e) -> arr_[off_ + i] = (char) e)); } -#end[short] +#end[strictShort] // End of low-level memory operations. @@ -5423,7 +5611,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return ARRAY_BASE + (((long)index) << ARRAY_SHIFT); } -#if[short] +#if[strictShort] static final int ARRAY_CHAR_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_CHAR_INDEX_SCALE); static final long ARRAY_CHAR_BASE = @@ -5433,7 +5621,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp static long charArrayAddress(char[] a, int index) { return ARRAY_CHAR_BASE + (((long)index) << ARRAY_CHAR_SHIFT); } -#end[short] +#end[strictShort] #if[byte] static final int ARRAY_BOOLEAN_SHIFT = @@ -5490,7 +5678,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /** * {@inheritDoc} -#if[byteOrShort] +#if[byte] * * @implNote This method always throws * {@code UnsupportedOperationException}, because there is no floating @@ -5498,23 +5686,27 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * of this method is arbitrarily designated as * {@code Vector}. Future versions of this API may change the return * type if additional floating point types become available. -#end[byteOrShort] +#end[byte] */ @ForceInline @Override public final - {#if[byteOrShort]?Vector:$Fptype$Vector} +#if[FP16] + $Type$Vector +#else[FP16] + {#if[byte]?Vector:$Fptype$Vector} +#end[FP16] viewAsFloatingLanes() { #if[FP] return this; #else[FP] LaneType flt = LaneType.$TYPE$.asFloating(); -#if[!byteOrShort] +#if[!byte] return ($Fptype$Vector) asVectorRaw(flt); -#else[!byteOrShort] +#else[!byte] // asFloating() will throw UnsupportedOperationException for the unsupported type $type$ throw new AssertionError("Cannot reach here"); -#end[!byteOrShort] +#end[!byte] #end[FP] } @@ -5531,10 +5723,19 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * {@code "[0,1,2...]"}, reporting the lane values of this vector, * in lane order. * +#if[FP16] + * The string is produced as if by a call to {@link + * java.util.Arrays#toString(Object[]) Arrays.toString()}, + * as appropriate to a {@code Float16} array whose elements + * are obtained by applying {@link Float16#shortBitsToFloat16(short)} + * to each element of the {@code short[]} array returned by + * {@link #toArray this.toArray()}. +#else[FP16] * The string is produced as if by a call to {@link * java.util.Arrays#toString($type$[]) Arrays.toString()}, * as appropriate to the {@code $type$} array returned by * {@link #toArray this.toArray()}. +#end[FP16] * * @return a string of the form {@code "[0,1,2...]"} * reporting the lane values of this vector @@ -5543,8 +5744,15 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp @ForceInline public final String toString() { +#if[FP16] + // Render the lanes as Float16 values; Float16.toString produces + // human-readable text and canonicalizes NaN, Infinity and -0.0 + // independent of the underlying bit encoding. + return Arrays.toString(toFloat16Array()); +#else[FP16] // now that toArray is strongly typed, we can define this return Arrays.toString(toArray()); +#end[FP16] } /** @@ -5570,8 +5778,14 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp @ForceInline public final int hashCode() { +#if[FP16] + // Hash the lanes as Float16 values; Float16.hashCode canonicalizes NaN + // so that all NaN representations contribute the same hash code. + return Objects.hash(species(), Arrays.hashCode(toFloat16Array())); +#else[FP16] // now that toArray is strongly typed, we can define this return Objects.hash(species(), Arrays.hashCode(toArray())); +#end[FP16] } // ================================================ @@ -5588,7 +5802,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp Class> maskType, Class> shuffleType, Function vectorFactory) { - super(shape, LaneType.of($type$.class), + super(shape, LaneType.of($elemtype$.class), vectorType, maskType, shuffleType, vectorFactory); assert(this.elementSize() == $Boxtype$.SIZE); @@ -5599,7 +5813,7 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp @Override @ForceInline public final Class<$Boxtype$> elementType() { - return $type$.class; + return $elemtype$.class; } @Override @@ -5656,8 +5870,8 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return value; #else[long] // Do the conversion, and then test it for failure. - $type$ e = ($type$) value; - if ((long) e != value) { + $type$ e = {#if[FP16]?float16ToRawShortBits(Float16.valueOf(value)):($type$) value}; + if ({#if[FP16]?shortBitsToFloat16(e).longValue():(long) e} != value) { throw badElementBits(value, e); } return toBits(e); @@ -5667,10 +5881,18 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp /*package-private*/ @ForceInline static long toIntegralChecked($type$ e, boolean convertToInt) { +#if[FP16] + float ef = shortBitsToFloat16(e).floatValue(); + long value = convertToInt ? (int) ef : (long) ef; + if ((float) value != ef) { + throw badArrayBits(e, convertToInt, value); + } +#else[FP16] long value = convertToInt ? (int) e : (long) e; if (($type$) value != e) { throw badArrayBits(e, convertToInt, value); } +#end[FP16] return value; } @@ -5682,11 +5904,19 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp $type$[] va = new $type$[laneCount()]; for (int i = 0; i < va.length; i++) { int lv = values[i]; +#if[FP16] + $type$ v = float16ToRawShortBits(Float16.valueOf(lv)); + va[i] = v; + if (Float16.valueOf(lv).intValue() != lv) { + throw badElementBits(lv, v); + } +#else[FP16] $type$ v = ($type$) lv; va[i] = v; if ((int)v != lv) { throw badElementBits(lv, v); } +#end[FP16] } return dummyVector().fromArray0(va, 0); } @@ -5859,10 +6089,17 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp } /** +#if[FP16] + * Finds a species for an element type of {@code $elemtype$} and shape. + * + * @param s the shape + * @return a species for an element type of {@code $elemtype$} and shape +#else[FP16] * Finds a species for an element type of {@code $type$} and shape. * * @param s the shape * @return a species for an element type of {@code $type$} and shape +#end[FP16] * @throws IllegalArgumentException if no such species exists for the shape */ static $Type$Species species(VectorShape s) { @@ -5922,6 +6159,6 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * A preferred species is a species of maximal bit-size for the platform. */ public static final VectorSpecies<$Boxtype$> SPECIES_PREFERRED - = ($Type$Species) VectorSpecies.ofPreferred($type$.class); + = ($Type$Species) VectorSpecies.ofPreferred($elemtype$.class); } 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 d66d22cab19..f8ac090a7f7 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 @@ -59,7 +59,7 @@ final class $vectortype$ extends $abstractvectortype$ { static final Class<$Carriertype$> CTYPE = $carriertype$.class; // carrier type used by the JVM - static final Class<$Boxtype$> ETYPE = $type$.class; // used by the JVM + static final Class<$Boxtype$> ETYPE = $elemtype$.class; // used by the JVM $vectortype$($type$[] v) { super(v); @@ -95,7 +95,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline @Override - public final Class<$Boxtype$> elementType() { return $type$.class; } + public final Class<$Boxtype$> elementType() { return $elemtype$.class; } @ForceInline final Class<$Carriertype$> carrierType() { return CTYPE; } @@ -216,6 +216,20 @@ final class $vectortype$ extends $abstractvectortype$ { super.uOpTemplate(($masktype$)m, f); // specialize } +#if[FP16] + @ForceInline + final @Override + $vectortype$ uRawOp(FUnRawOp f) { + return ($vectortype$) super.uRawOpTemplate(f); // specialize + } + + @ForceInline + final @Override + $vectortype$ uRawOp(VectorMask<$Boxtype$> m, FUnRawOp f) { + return ($vectortype$) + super.uRawOpTemplate(($masktype$)m, f); // specialize + } +#end[FP16] // Binary operator @ForceInline @@ -574,6 +588,24 @@ final class $vectortype$ extends $abstractvectortype$ { case 13: bits = laneHelper(13); break; case 14: bits = laneHelper(14); break; case 15: bits = laneHelper(15); break; +#if[!16L] + case 16: bits = laneHelper(16); break; + case 17: bits = laneHelper(17); break; + case 18: bits = laneHelper(18); break; + case 19: bits = laneHelper(19); break; + case 20: bits = laneHelper(20); break; + case 21: bits = laneHelper(21); break; + case 22: bits = laneHelper(22); break; + case 23: bits = laneHelper(23); break; + case 24: bits = laneHelper(24); break; + case 25: bits = laneHelper(25); break; + case 26: bits = laneHelper(26); break; + case 27: bits = laneHelper(27); break; + case 28: bits = laneHelper(28); break; + case 29: bits = laneHelper(29); break; + case 30: bits = laneHelper(30); break; + case 31: bits = laneHelper(31); break; +#end[!16L] #end[!8L] #end[!4L] #end[!2L] @@ -586,7 +618,7 @@ final class $vectortype$ extends $abstractvectortype$ { } $bitstype$ bits = laneHelper(i); #end[!Max] - return $Type$.$bitstype$BitsTo$Fptype$(bits); + return {#if[FP16]?bits:$Type$.$bitstype$BitsTo$Fptype$(bits)}; } @ForceInline @@ -596,7 +628,7 @@ final class $vectortype$ extends $abstractvectortype$ { this, i, (vec, ix) -> { $type$[] vecarr = vec.vec(); - return (long)$Type$.$type$ToRaw$Bitstype$Bits(vecarr[ix]); + return {#if[FP16]?vecarr[ix]:(long)$Type$.$type$ToRaw$Bitstype$Bits(vecarr[ix])}; }); } @@ -625,6 +657,24 @@ final class $vectortype$ extends $abstractvectortype$ { case 13: return withLaneHelper(13, e); case 14: return withLaneHelper(14, e); case 15: return withLaneHelper(15, e); +#if[!16L] + case 16: return withLaneHelper(16, e); + case 17: return withLaneHelper(17, e); + case 18: return withLaneHelper(18, e); + case 19: return withLaneHelper(19, e); + case 20: return withLaneHelper(20, e); + case 21: return withLaneHelper(21, e); + case 22: return withLaneHelper(22, e); + case 23: return withLaneHelper(23, e); + case 24: return withLaneHelper(24, e); + case 25: return withLaneHelper(25, e); + case 26: return withLaneHelper(26, e); + case 27: return withLaneHelper(27, e); + case 28: return withLaneHelper(28, e); + case 29: return withLaneHelper(29, e); + case 30: return withLaneHelper(30, e); + case 31: return withLaneHelper(31, e); +#end[!16L] #end[!8L] #end[!4L] #end[!2L] @@ -643,10 +693,10 @@ final class $vectortype$ extends $abstractvectortype$ { public $vectortype$ withLaneHelper(int i, $type$ e) { return VectorSupport.insert( VCLASS, LANE_TYPE_ORDINAL, VLENGTH, - this, i, (long)$Type$.$type$ToRaw$Bitstype$Bits(e), + this, i, (long){#if[FP16]?e:$Type$.$type$ToRaw$Bitstype$Bits(e)}, (v, ix, bits) -> { $type$[] res = v.vec().clone(); - res[ix] = $Type$.$bitstype$BitsTo$Type$(($bitstype$)bits); + res[ix] = {#if[FP16]?($bitstype$)bits:$Type$.$bitstype$BitsTo$Type$(($bitstype$)bits)}; return v.vectorFactory(res); }); } @@ -981,7 +1031,7 @@ final class $vectortype$ extends $abstractvectortype$ { public $masktype$ compress() { return ($masktype$)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, $vectortype$.class, $masktype$.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, - (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); + (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, {#if[FP16]?Float16.float16ToRawShortBits(Float16.valueOf(m1.trueCount())):m1.trueCount()})); } @@ -1389,7 +1439,7 @@ final class $vectortype$ extends $abstractvectortype$ { return super.fromArray0Template($masktype$.class, a, offset, indexMap, mapOffset, ($masktype$) m); } -#if[short] +#if[strictShort] @ForceInline @Override final @@ -1403,7 +1453,7 @@ final class $vectortype$ extends $abstractvectortype$ { $abstractvectortype$ fromCharArray0(char[] a, int offset, VectorMask<$Boxtype$> m, int offsetInRange) { return super.fromCharArray0Template($masktype$.class, a, offset, ($masktype$) m, offsetInRange); // specialize } -#end[short] +#end[strictShort] #if[byte] @ForceInline @@ -1474,14 +1524,14 @@ final class $vectortype$ extends $abstractvectortype$ { super.intoMemorySegment0Template($masktype$.class, ms, offset, ($masktype$) m); } -#if[short] +#if[strictShort] @ForceInline @Override final void intoCharArray0(char[] a, int offset, VectorMask<$Boxtype$> m) { super.intoCharArray0Template($masktype$.class, a, offset, ($masktype$) m); } -#end[short] +#end[strictShort] // End of specialized low-level memory operations. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh index a8a7ea83625..7735085a16b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh @@ -53,19 +53,29 @@ typeprefix= globalArgs="" #globalArgs="$globalArgs -KextraOverrides" -for type in byte short int long float double +for type in byte short int long float double float16 do Type="$(tr '[:lower:]' '[:upper:]' <<< ${type:0:1})${type:1}" TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${type})" + + case $type in + float16) + type=short + TYPE=SHORT + ;; + esac + args=$globalArgs args="$args -K$type -Dtype=$type -DType=$Type -DTYPE=$TYPE" Boxtype=$Type Wideboxtype=$Boxtype + ElemLayout=$Type kind=BITWISE bitstype=$type + maskbitstype=$type Bitstype=$Type Boxbitstype=$Boxtype @@ -74,23 +84,28 @@ do Boxfptype=$Boxtype carriertype=$type Carriertype=$Type + elemtype=$type + fallbacktype=$type - case $type in - byte) + case $Type in + Byte) Wideboxtype=Integer sizeInBytes=1 laneType=LT_BYTE lanebitsType=LT_BYTE - args="$args -KbyteOrShort" + args="$args -KbyteOrShort -KbyteOrStrictShort" ;; - short) + Short) + fptype=Float16 + Fptype=Float16 + Boxfptype=Float16 Wideboxtype=Integer sizeInBytes=2 laneType=LT_SHORT lanebitsType=LT_SHORT - args="$args -KbyteOrShort" + args="$args -KbyteOrShort -KbyteOrStrictShort -KstrictShort" ;; - int) + Int) Boxtype=Integer Carriertype=Integer Wideboxtype=Integer @@ -103,7 +118,7 @@ do lanebitsType=LT_INT args="$args -KintOrLong -KintOrFP -KintOrFloat" ;; - long) + Long) fptype=double Fptype=Double Boxfptype=Double @@ -112,33 +127,53 @@ do lanebitsType=LT_LONG args="$args -KintOrLong -KlongOrDouble" ;; - float) + Float) kind=FP bitstype=int + maskbitstype=int Bitstype=Int Boxbitstype=Integer sizeInBytes=4 laneType=LT_FLOAT lanebitsType=LT_INT - args="$args -KintOrFP -KintOrFloat" + args="$args -KFP32 -KintOrFP -KintOrFloat" ;; - double) + Double) kind=FP bitstype=long + maskbitstype=long Bitstype=Long Boxbitstype=Long sizeInBytes=8 laneType=LT_DOUBLE lanebitsType=LT_LONG - args="$args -KintOrFP -KlongOrDouble" + args="$args -KFP64 -KintOrFP -KlongOrDouble" + ;; + Float16) + kind=FP + bitstype=short + maskbitstype=short + Bitstype=Short + Boxbitstype=Short + sizeInBytes=2 + carriertype=short + Carriertype=Short + Boxtype=Float16 + elemtype=Float16 + ElemLayout=Short + laneType=LT_FLOAT16 + lanebitsType=LT_SHORT + fallbacktype=float + args="$args -KFP16 -KbyteOrShort" ;; esac - args="$args -K$kind -DlaneType=$laneType -DlanebitsType=$lanebitsType -DBoxtype=$Boxtype -DWideboxtype=$Wideboxtype" - args="$args -Dbitstype=$bitstype -DBitstype=$Bitstype -DBoxbitstype=$Boxbitstype" + + args="$args -K$kind -DlaneType=$laneType -DlanebitsType=$lanebitsType -Dfallbacktype=$fallbacktype -DBoxtype=$Boxtype -DWideboxtype=$Wideboxtype" + args="$args -DElemLayout=$ElemLayout -Dbitstype=$bitstype -Dmaskbitstype=$maskbitstype -DBitstype=$Bitstype -DBoxbitstype=$Boxbitstype" args="$args -Dfptype=$fptype -DFptype=$Fptype -DBoxfptype=$Boxfptype" args="$args -DsizeInBytes=$sizeInBytes" - args="$args -Dcarriertype=$carriertype -DCarriertype=$Carriertype" + args="$args -Dcarriertype=$carriertype -Delemtype=$elemtype -DCarriertype=$Carriertype" abstractvectortype=${typeprefix}${Type}Vector abstractbitsvectortype=${typeprefix}Vector${Bitstype} diff --git a/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java b/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java index d2c0fa29877..c0733e65a74 100644 --- a/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java +++ b/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.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 @@ -75,6 +75,7 @@ public class PlatformSupportImpl extends PlatformSupport { * It is important that this directory is well-known and the * same for all VM instances. It cannot be affected by configuration * variables such as java.io.tmpdir. + * It can be affected by VM option -XX:AltTempDir, however. * * Implementation Details: * @@ -170,8 +171,8 @@ public class PlatformSupportImpl extends PlatformSupport { /* - * Extract either the host PID or the NameSpace PID - * from a file path. + * Extract the VM ID (pid) from a file path, + * specifically the host pid for a container process. * * File path should be in 1 of these 2 forms: * @@ -179,6 +180,8 @@ public class PlatformSupportImpl extends PlatformSupport { * or * /tmp/hsperfdata_{user}/{pid} * + * (where /tmp may be substituted due to -XX:AltTempDir) + * * In either case we want to return {pid} and NOT {nspid} * * This function filters out host pids which do not have @@ -189,25 +192,39 @@ public class PlatformSupportImpl extends PlatformSupport { */ public int getLocalVmId(File file) throws NumberFormatException { String p = file.getAbsolutePath(); - String s[] = p.split("\\/"); + String procParts[] = p.split("\\/"); // "/proc/hostpid/root//hsperfdata_user/nsid" - // Determine if this file is from a container - if (s.length == 7 && s[1].equals("proc")) { - int hostpid = Integer.parseInt(s[2]); - int nspid = Integer.parseInt(s[6]); - if (nspid == hostpid || nspid == getNamespaceVmId(hostpid)) { - return hostpid; - } - else { - return -1; - } + int hostpid = -1; + int nspid = -1; + + // ["", "proc", "hostpid", "root", "tmpdir" .. "tmpdir", "hsperfdata_user", "nsid"] + if (procParts.length > 4 && procParts[1].equals("proc") && procParts[3].equals("root")) { + hostpid = Integer.parseInt(procParts[2]); } - else { - return Integer.parseInt(file.getName()); + + // Some invalid path. + if (procParts.length < 2) { + return -1; + } + + // Path at the end after tmp dir is: "hsperfdata_username/PID" + int end = procParts.length - 1; + if (!procParts[end-1].startsWith("hsperfdata_")) { + return -1; + } + if (hostpid == -1) { + hostpid = Integer.parseInt(procParts[end]); + } else { + nspid = Integer.parseInt(procParts[end]); + } + if (nspid == -1) { + return hostpid; + } else { + // We have both pids. + return nspid == getNamespaceVmId(hostpid) ? hostpid : -1; } } - /* * Return the inner most namespaced PID if there is one, * otherwise return the original PID. diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties index f780bd1f1c3..62ef135cb8a 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties @@ -104,7 +104,7 @@ jar.verified.with.signer.errors.=jar 已验证, 但出现签名者错误。 history.with.ts=- 由 "%1$s" 签名\n 摘要算法: %2$s\n 签名算法: %3$s, %4$s\n 由 "%6$s" 于 %5$tc 加时间戳\n 时间戳摘要算法: %7$s\n 时间戳签名算法: %8$s, %9$s history.without.ts=- 由 "%1$s" 签名\n 摘要算法: %2$s\n 签名算法: %3$s, %4$s -history.nonexistent.entries=\ 警告:不存在的签名条目:\u0020 +history.nonexistent.entries=\ 警告:不存在的签名条目: history.unparsable=- 无法解析的与签名相关的文件 %s history.nosf=- 缺少与签名相关的文件 META-INF/%s.SF history.nobk=- 与签名相关的文件 META-INF/%s.SF 缺少块文件 @@ -119,13 +119,13 @@ key.bit.disabled=%s 密钥(禁用) nonexistent.entries.found=此 jar 的文件包含不存在的签名条目。有关更多详细信息,请参见 -verbose 输出。 external.file.attributes.detected=检测到 POSIX 文件权限和/或 symlink 属性。这些属性在进行签名时会被忽略,不受该签名的保护。 -jarsigner.=jarsigner:\u0020 +jarsigner.=jarsigner: signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.=签名文件名必须包含以下字符: A-Z, 0-9, _ 或 - unable.to.open.jar.file.=无法打开 jar 文件:\u0020 unable.to.create.=无法创建:\u0020 -.adding.=\ 正在添加:\u0020 -.updating.=\ 正在更新:\u0020 -.signing.=\ 正在签名:\u0020 +.adding.=\ 正在添加: +.updating.=\ 正在更新: +.signing.=\ 正在签名: attempt.to.rename.signedJarFile.to.jarFile.failed=尝试将{0}重命名为{1}时失败 attempt.to.rename.jarFile.to.origJar.failed=尝试将{0}重命名为{1}时失败 unable.to.sign.jar.=无法对 jar 进行签名:\u0020 @@ -156,8 +156,8 @@ no.response.from.the.Timestamping.Authority.=时间戳颁发机构没有响应 or=或 Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the=找不到{0}的证书。{1}必须引用包含时间戳颁发机构的 X.509 公共密钥证书的有效密钥库条目。 entry.was.signed.on=条目的签名日期为 {0} -Warning.=警告:\u0020 -Error.=错误:\u0020 +Warning.=警告: +Error.=错误: ...Signer=>>> 签名者 ...TSA=>>> TSA trusted.certificate=可信证书 diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties index 292ec9c963d..2b4c8c27261 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties @@ -82,6 +82,8 @@ error.validator.info.version.notequal={0}: module-info.class in einem versionier error.validator.info.manclass.notequal={0}: module-info.class in einem versionierten Verzeichnis enthält unterschiedlichen "main-class"-Wert error.validator.metainf.wrong.position=Eintrag META-INF/ an Position 0 erwartet, aber an Position {0} gefunden error.validator.manifest.wrong.position=Eintrag META-INF/MANIFEST.MF an Position 0 oder 1 erwartet, aber an Position {0} gefunden +error.validator.manifest.invalid.automatic.module.name=Ungültiger Modulname des "Automatic-Module-Name"-Eintrags in Manifest: {0} +error.validator.manifest.inconsistent.automatic.module.name=Es wird erwartet, dass der "Automatic-Module-Name"-Eintrag in Manifest "{0}" mit dem Namen des kompilierten Moduls "{1}" übereinstimmt warn.validator.identical.entry=Warnung: Eintrag {0} enthält eine Klasse, die mit\neinem bereits in der JAR-Datei enthaltenen Eintrag identisch ist warn.validator.resources.with.same.name=Warnung: Eintrag {0}, mehrere Ressourcen mit demselben Namen warn.validator.concealed.public.class=Warnung: Eintrag {0} ist eine öffentliche Klasse\nin einem verdeckten Package. Wenn Sie diese JAR-Datei in den Classpath einfügen, kommt es\nzu nicht kompatiblen öffentlichen Schnittstellen diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties index 0d0f91ad791..4ea0130846d 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties @@ -82,6 +82,8 @@ error.validator.info.version.notequal={0}: バージョニングされたディ error.validator.info.manclass.notequal={0}: バージョニングされたディレクトリのmodule-info.classに異なる"main-class"が含まれています error.validator.metainf.wrong.position=エントリMETA-INF/は0の位置にある必要がありますが、見つかりました: {0} error.validator.manifest.wrong.position=エントリMETA-INF/MANIFEST.MFは0または1の位置にある必要がありますが、位置: {0}で見つかりました +error.validator.manifest.invalid.automatic.module.name=マニフェスト内のAutomatic-Module-Nameエントリのモジュール名が無効です: {0} +error.validator.manifest.inconsistent.automatic.module.name=マニフェスト内のAutomatic-Module-Nameエントリ: {0}は、コンパイル済モジュールの名前と一致する必要があります: {1} warn.validator.identical.entry=警告 : エントリ{0}には、jarにすでに存在する\nエントリと同じクラスが含まれます warn.validator.resources.with.same.name=警告 : エントリ{0}、同じ名前を持つ複数のリソース warn.validator.concealed.public.class=警告 : エントリ{0}は、隠しパッケージ内のpublicクラスです。\nクラスパスにこのjarを配置すると、互換性のない\npublicインタフェースが生成されます diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties index 41833d28bfc..0eedd48e05d 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties @@ -82,6 +82,8 @@ error.validator.info.version.notequal={0}: 版本化目录中的 module-info.cla error.validator.info.manclass.notequal={0}: 版本化目录中的 module-info.class 包含不同的 "main-class" error.validator.metainf.wrong.position=条目 META-INF/ 应位于位置 0 处,但发现:{0} error.validator.manifest.wrong.position=条目 META-INF/MANIFEST.MF 应位于位置 0 或 1 处,但发现该条目位于位置 {0} 处 +error.validator.manifest.invalid.automatic.module.name=清单 {0} 中 Automatic-Module-Name 条目的模块名称无效 +error.validator.manifest.inconsistent.automatic.module.name=需要清单 {0} 中的 Automatic-Module-Name 条目才能与已编译模块 {1} 的名称匹配 warn.validator.identical.entry=警告: 条目 {0} 包含与 jar 中的\n现有条目相同的类 warn.validator.resources.with.same.name=警告: 条目 {0}, 多个资源具有相同名称 warn.validator.concealed.public.class=警告: 条目 {0} 是已隐藏程序包中的\n公共类, 将此 jar 放置在类路径中\n将导致公共接口不兼容 diff --git a/src/jdk.jartool/share/man/jarsigner.md b/src/jdk.jartool/share/man/jarsigner.md index d128b9c11ff..b24382fdda5 100644 --- a/src/jdk.jartool/share/man/jarsigner.md +++ b/src/jdk.jartool/share/man/jarsigner.md @@ -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 @@ -181,11 +181,7 @@ Currently, there are two command-line tools that use keystore implementations The default keystore implementation is `PKCS12`. This is a cross platform keystore based on the RSA PKCS12 Personal Information Exchange Syntax Standard. This standard is primarily meant for storing or transporting a user's private -keys, certificates, and miscellaneous secrets. There is another built-in -implementation, provided by Oracle. It implements the keystore as a file with a -proprietary keystore type (format) named `JKS`. It protects each private key -with its individual password, and also protects the integrity of the entire -keystore with a (possibly different) password. +keys, certificates, and miscellaneous secrets. Keystore implementations are provider-based, which means the application interfaces supplied by the `KeyStore` class are implemented in terms of a @@ -237,15 +233,11 @@ specified by the following line in the security properties file: > `keystore.type=pkcs12` -Case doesn't matter in keystore type designations. For example, `JKS` is the -same as `jks`. +Case doesn't matter in keystore type designations. For example, `PKCS12` is the +same as `pkcs12`. To have the tools utilize a keystore implementation other than the default, you -can change that line to specify a different keystore type. For example, if you -want to use the Oracle's `jks` keystore implementation, then change the line to -the following: - -> `keystore.type=jks` +can change that line to specify a different keystore type. ## Supported Algorithms diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties index a380b29d553..999d11bed3d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties @@ -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 @@ -178,7 +178,8 @@ doclet.Inheritance_Tree=Vererbungsbaum doclet.DefinedIn=Definiert in doclet.ReferencedIn=Referenziert in doclet.External_Specifications=Externe Spezifikationen -doclet.External_Specifications.All_Specifications=Alle Spezifikationen +doclet.External_Specifications.by-host=Spezifikationen nach Hostnamen anzeigen: +doclet.External_Specifications.all-hosts=Alle Hostnamen doclet.External_Specifications.no-host=Lokal doclet.Specification=Spezifikation doclet.Summary_Page=Zusammenfassung (Seite) @@ -388,7 +389,7 @@ doclet.usage.version.description=@version-Absätze aufnehmen doclet.usage.author.description=@author-Absätze aufnehmen -doclet.usage.docfilessubdirs.description=Ermöglicht Deep Copying von "doc-files"-Verzeichnissen. Unterverzeichnisse und alle\nInhalte werden rekursiv in das Ziel kopiert +doclet.usage.docfilessubdirs.description=Die Option "-docfilessubdirs" ist nicht mehr erforderlich und\nwird möglicherweise in einem zukünftigen Release entfernt. doclet.usage.splitindex.description=Index in eine Datei pro Buchstabe aufteilen @@ -432,7 +433,7 @@ doclet.usage.link-platform-properties.parameters=< URL> doclet.usage.link-platform-properties.description=Link zu Plattformdokumentations-URLs, die in der Eigenschaftendatei auf deklariert sind doclet.usage.excludedocfilessubdir.parameters=,,... -doclet.usage.excludedocfilessubdir.description=Schließen Sie alle "doc-files"-Unterverzeichnisse mit einem angegebenen Namen aus.\n":" kann überall im Argument als Trennzeichen verwendet werden. +doclet.usage.excludedocfilessubdir.description=Schließen Sie alle "doc-files"-Unterverzeichnisse mit dem angegebenen Namen aus.\nVerwenden Sie "*", um alle Unterverzeichnisse auszuschließen. ":" kann\nüberall im Argument als Trennzeichen verwendet werden. doclet.usage.group.parameters= ,... doclet.usage.group.description=Angegebene Packages oder Module auf Überblickseite gruppieren.\n":" kann überall im Argument als Trennzeichen verwendet werden. @@ -544,4 +545,7 @@ doclet.NoFrames_specified=Die Option --no-frames wird nicht mehr benötigt und w # L10N: do not localize the option name -footer doclet.footer_specified=Die Option -footer wird nicht mehr unterstützt und wird ignoriert.\nSie wird möglicherweise in einem zukünftigen Release entfernt. +# L10N: do not localize the option name -docfilessubdirs +doclet.docfilessubdirs_specified=Hinweis: Die Option "-docfilessubdirs" ist nicht mehr erforderlich und\nwird möglicherweise in einem zukünftigen Release entfernt. + doclet.selectModule=Wählen Sie das Modul aus, in dem gesucht werden soll. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties index 69cdc862b4c..c4b8b1e099c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties @@ -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 @@ -178,7 +178,8 @@ doclet.Inheritance_Tree=継承ツリー doclet.DefinedIn=定義先 doclet.ReferencedIn=参照 doclet.External_Specifications=外部仕様 -doclet.External_Specifications.All_Specifications=すべての仕様 +doclet.External_Specifications.by-host=ホスト名別に仕様を表示: +doclet.External_Specifications.all-hosts=すべてのホスト名 doclet.External_Specifications.no-host=ローカル doclet.Specification=仕様 doclet.Summary_Page=サマリー・ページ @@ -388,7 +389,7 @@ doclet.usage.version.description=@versionパラグラフを含めます doclet.usage.author.description=@authorパラグラフを含めます -doclet.usage.docfilessubdirs.description='doc-files'ディレクトリのディープ・コピーを有効にします。\n宛先には、サブディレクトリとそのすべて内容が再帰的にコピーされます +doclet.usage.docfilessubdirs.description=-docfilessubdirsオプションは必須ではなくなり、\n将来のリリースで削除される可能性があります。 doclet.usage.splitindex.description=1字ごとに1ファイルに索引を分割します @@ -432,7 +433,7 @@ doclet.usage.link-platform-properties.parameters= doclet.usage.link-platform-properties.description=にあるプロパティ・ファイルで宣言されているプラットフォーム・ドキュメントのURLにリンクします doclet.usage.excludedocfilessubdir.parameters=,,... -doclet.usage.excludedocfilessubdir.description=指定された名前の'doc-files'サブディレクトリをすべて除外します。\n':'も、セパレータとして引数の任意の場所に使用できます。 +doclet.usage.excludedocfilessubdir.description=指定された名前の'doc-files'サブディレクトリをすべて除外します。\nすべてのサブディレクトリを除外するには'*'を使用します。':'も、セパレータ\nとして引数の任意の場所に使用できます。 doclet.usage.group.parameters= ,... doclet.usage.group.description=指定するパッケージまたはモジュールを概要ページにおいてグループ化します。\n':'も、セパレータとして引数の任意の場所に使用できます。 @@ -544,4 +545,7 @@ doclet.NoFrames_specified=--no-framesオプションは必須ではなくなり # L10N: do not localize the option name -footer doclet.footer_specified=-footerオプションはサポートされなくなったため、無視されます。\n将来のリリースで削除される可能性があります。 +# L10N: do not localize the option name -docfilessubdirs +doclet.docfilessubdirs_specified=ノート: -docfilessubdirsオプションは必須ではなくなり、\n将来のリリースで削除される可能性があります。 + doclet.selectModule=検索するモジュールを選択します。 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties index b3a0a3a1197..f1e14ebedd8 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties @@ -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 @@ -178,7 +178,8 @@ doclet.Inheritance_Tree=继承树 doclet.DefinedIn=定义位置 doclet.ReferencedIn=参考位置 doclet.External_Specifications=外部规范 -doclet.External_Specifications.All_Specifications=所有规范 +doclet.External_Specifications.by-host=按主机名显示规范: +doclet.External_Specifications.all-hosts=所有主机名 doclet.External_Specifications.no-host=本地 doclet.Specification=规范 doclet.Summary_Page=概要页 @@ -388,7 +389,7 @@ doclet.usage.version.description=包含 @version 段 doclet.usage.author.description=包含 @author 段 -doclet.usage.docfilessubdirs.description=启用对 'doc-files' 目录的深层复制。\n子目录和所有内容将递归复制到目标 +doclet.usage.docfilessubdirs.description=-docfilessubdirs 选项不再是必需的,可能\n会在未来发行版中删除此选项。 doclet.usage.splitindex.description=将索引分为每个字母对应一个文件 @@ -432,7 +433,7 @@ doclet.usage.link-platform-properties.parameters= doclet.usage.link-platform-properties.description=链接到位于 的属性文件中声明的平台文档 URL doclet.usage.excludedocfilessubdir.parameters=,,... -doclet.usage.excludedocfilessubdir.description=排除包含给定名称的所有 'doc-files' 子目录。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 +doclet.usage.excludedocfilessubdir.description=排除包含给定名称的所有 'doc-files' 子目录。\n使用 '*' 排除所有子目录。还可以将 ':' 作为\n分隔符用于参数中的任何位置。 doclet.usage.group.parameters= ,... doclet.usage.group.description=在概览页面中将指定程序包或模块归到一组。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 @@ -544,4 +545,7 @@ doclet.NoFrames_specified=--no-frames 选项不再是必需的,可能\n会在 # L10N: do not localize the option name -footer doclet.footer_specified=-footer 选项不再受支持并将被忽略。\n可能会在未来发行版中删除此选项。 +# L10N: do not localize the option name -docfilessubdirs +doclet.docfilessubdirs_specified=注:-docfilessubdirs 选项不再是必需的,可能\n会在未来发行版中删除此选项。 + doclet.selectModule=选择要在其中搜索的模块。 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties index a6dbf050bf3..7f262141961 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties @@ -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 @@ -318,8 +318,10 @@ doclet.search.many_results={0} Ergebnisse gefunden doclet.search.loading=Suchindex wird geladen... doclet.search.searching=Suche wird ausgeführt... doclet.search.redirecting=Zum ersten Ergebnis wird umgeleitet... -# {0} is a select input containing all_modules message below and module names -doclet.search.in=in {0} +# Used as label for the search input field +doclet.search.for=Suchen nach +# Used as label for the module select control which defaults to doclet.search.all_modules +doclet.search.in_modules=in doclet.search.all_modules=allen Modulen doclet.search.modules=Module doclet.search.packages=Packages @@ -327,7 +329,8 @@ doclet.search.classes_and_interfaces=Klassen und Schnittstellen doclet.search.types=Typen doclet.search.members=Mitglieder doclet.search.search_tags=Tags suchen -doclet.search.linkSearchPageLabel=Gehe zur Suchseite +doclet.search.linkSearchPageLabel=Suchseite +doclet.search.linkSearchHelpLabel=Hilfe durchsuchen doclet.snippet.contents.none=@snippet gibt keinen Inhalt an diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties index 1970203da38..10282979ee4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties @@ -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 @@ -318,8 +318,10 @@ doclet.search.many_results={0}の結果が見つかりました doclet.search.loading=検索索引をロード中... doclet.search.searching=検索中... doclet.search.redirecting=最初の結果にリダイレクト中... -# {0} is a select input containing all_modules message below and module names -doclet.search.in={0}内 +# Used as label for the search input field +doclet.search.for=検索対象 +# Used as label for the module select control which defaults to doclet.search.all_modules +doclet.search.in_modules=対象 doclet.search.all_modules=すべてのモジュール doclet.search.modules=モジュール doclet.search.packages=パッケージ @@ -327,7 +329,8 @@ doclet.search.classes_and_interfaces=クラスとインタフェース doclet.search.types=タイプ doclet.search.members=メンバー doclet.search.search_tags=タグの検索 -doclet.search.linkSearchPageLabel=検索ページに移動します +doclet.search.linkSearchPageLabel=検索ページ +doclet.search.linkSearchHelpLabel=検索ヘルプ doclet.snippet.contents.none=@snippetにコンテンツが指定されていません diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties index 62e51c2c1c4..cde88e840db 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties @@ -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 @@ -318,8 +318,10 @@ doclet.search.many_results=找到 {0} 个结果 doclet.search.loading=正在加载搜索索引... doclet.search.searching=正在搜索... doclet.search.redirecting=正在重定向到第一个结果... -# {0} is a select input containing all_modules message below and module names -doclet.search.in=在 {0} 中 +# Used as label for the search input field +doclet.search.for=搜索 +# Used as label for the module select control which defaults to doclet.search.all_modules +doclet.search.in_modules=位于 doclet.search.all_modules=全部模块 doclet.search.modules=模块 doclet.search.packages=程序包 @@ -327,7 +329,8 @@ doclet.search.classes_and_interfaces=类和接口 doclet.search.types=类型 doclet.search.members=成员 doclet.search.search_tags=搜索标记 -doclet.search.linkSearchPageLabel=转至搜索页 +doclet.search.linkSearchPageLabel=搜索页 +doclet.search.linkSearchHelpLabel=搜索帮助 doclet.snippet.contents.none=@snippet 未指定内容 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java index 0b3b767ea39..ce287234922 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java @@ -2525,7 +2525,7 @@ public class Utils { usedInDeclaration.addAll(types2Classes(tpe.getBounds())); } usedInDeclaration.addAll(types2Classes(List.of(te.getSuperclass()))); - usedInDeclaration.addAll(types2Classes(te.getPermittedSubclasses())); + // Intentionally allow preview permitted subclasses usedInDeclaration.addAll(types2Classes(te.getRecordComponents().stream().map(Element::asType).toList())); //TODO: annotations on record components??? } case CONSTRUCTOR, METHOD -> { diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index 23dfa67d864..97e7385138b 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -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 @@ -77,8 +77,12 @@ jcmd - send diagnostic command requests to a running Java Virtual Machine The `jcmd` utility is used to send diagnostic command requests to the JVM. It must be used on the same machine on which the JVM is running, and have the same -effective user and group identifiers that were used to launch the JVM. Each -diagnostic command has its own set of options and arguments. To display the description, +effective user and group identifiers that were used to launch the JVM. Both must +use the same temporary file location for communication; this is true by default +but also see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option that can be +set for the JVM. + +Each diagnostic command has its own set of options and arguments. To display the description, syntax, and a list of available options and arguments for a diagnostic command, use the name of the command as the argument. For example: diff --git a/src/jdk.jcmd/share/man/jinfo.md b/src/jdk.jcmd/share/man/jinfo.md index b70bc4c45ee..cc582aa9d5c 100644 --- a/src/jdk.jcmd/share/man/jinfo.md +++ b/src/jdk.jcmd/share/man/jinfo.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -50,10 +50,7 @@ jinfo - generate Java configuration information for a specified Java process The `jinfo` command prints Java configuration information for a specified Java process. The configuration information includes Java system properties and JVM -command-line flags. If the specified process is running on a 64-bit JVM, then -you might need to specify the `-J-d64` option, for example: - -> `jinfo -J-d64 -sysprops` *pid* +command-line flags. This command is unsupported and might not be available in future releases of the JDK. In Windows Systems where `dbgeng.dll` is not present, the Debugging @@ -62,6 +59,10 @@ environment variable should contain the location of the `jvm.dll` that's used by the target process or the location from which the core dump file was produced. +If the target JVM is started with an alternate temporary file location, `jinfo` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + ## Options for the jinfo Command **Note:** diff --git a/src/jdk.jcmd/share/man/jmap.md b/src/jdk.jcmd/share/man/jmap.md index dd0be1b24ef..2fe766c25b5 100644 --- a/src/jdk.jcmd/share/man/jmap.md +++ b/src/jdk.jcmd/share/man/jmap.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -60,6 +60,11 @@ Debugging Tools for Windows must be installed to make these tools work. The that's used by the target process or the location from which the core dump file was produced. +If the target JVM is started with an alternate temporary file location, `jmap` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + + ## Options for the jmap Command [`-clstats`]{#option-clstats} *pid* diff --git a/src/jdk.jcmd/share/man/jps.md b/src/jdk.jcmd/share/man/jps.md index 2db93878801..cdd5e8e8b9d 100644 --- a/src/jdk.jcmd/share/man/jps.md +++ b/src/jdk.jcmd/share/man/jps.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -99,6 +99,9 @@ permissions granted to the principal running the command. The command lists only the JVMs for which the principal has access rights as determined by operating system-specific access control mechanisms. +The list of JVMs is also limited to those that use the same temporary file location as the `jps` +command. That is normally the case but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + ## Host Identifier The host identifier, or `hostid`, is a string that indicates the target system. diff --git a/src/jdk.jcmd/share/man/jstack.md b/src/jdk.jcmd/share/man/jstack.md index 15849502d8c..b2cf02c6eb5 100644 --- a/src/jdk.jcmd/share/man/jstack.md +++ b/src/jdk.jcmd/share/man/jstack.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -52,8 +52,7 @@ The `jstack` command prints Java stack traces of Java threads for a specified Java process. For each Java frame, the full class name, method name, byte code index (BCI), and line number, when available, are printed. C++ mangled names aren't demangled. To demangle C++ names, the output of this command can be -piped to `c++filt`. When the specified process is running on a 64-bit JVM, you -might need to specify the `-J-d64` option, for example: `jstack -J-d64` *pid*. +piped to `c++filt`. **Note:** @@ -64,6 +63,11 @@ Debugging Tools for Windows must be installed so that these tools work. The is used by the target process, or the location from which the core dump file was produced. +If the target JVM is started with an alternate temporary file location, `jstack` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + + ## Options for the jstack Command `-l` diff --git a/src/jdk.jcmd/share/man/jstat.md b/src/jdk.jcmd/share/man/jstat.md index 624b675de76..4b686f73810 100644 --- a/src/jdk.jcmd/share/man/jstat.md +++ b/src/jdk.jcmd/share/man/jstat.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -85,6 +85,11 @@ statistical output. All options and their functionality are subject to change or removal in future releases. +If the target JVM is started with an alternate temporary file location, `jstat` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + + ## General Options If you specify one of the general options, then you can't specify any other diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java index e3071c27064..7957834e960 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.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 @@ -29,6 +29,7 @@ import static com.sun.tools.jdeps.Module.trace; import static java.util.stream.Collectors.*; import java.io.BufferedInputStream; +import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -41,6 +42,7 @@ import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; import java.net.URI; +import java.net.URLClassLoader; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -282,6 +284,7 @@ public class JdepsConfiguration implements AutoCloseable { archive.close(); for (Module module : nameToModule.values()) module.close(); + system.close(); } static class SystemModuleFinder implements ModuleFinder { @@ -290,6 +293,7 @@ public class JdepsConfiguration implements AutoCloseable { private final FileSystem fileSystem; private final Path root; private final Map systemModules; + private final List closeables = new ArrayList<>(); SystemModuleFinder() { if (Files.isRegularFile(Paths.get(JAVA_HOME, "lib", "modules"))) { @@ -321,6 +325,11 @@ public class JdepsConfiguration implements AutoCloseable { env.put("java.home", javaHome); // a remote run-time image this.fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), env); + closeables.add(fileSystem); + ClassLoader cl = fileSystem.provider().getClass().getClassLoader(); + if (cl instanceof URLClassLoader urlcl) { + closeables.add(urlcl); + } this.root = fileSystem.getPath("/modules"); this.systemModules = walk(root); } @@ -420,6 +429,24 @@ public class JdepsConfiguration implements AutoCloseable { .map(ModuleDescriptor::name) .collect(Collectors.toSet()); } + + public void close() throws IOException { + IOException ioe = null; + for (Closeable closeable : closeables) { + try { + closeable.close(); + } catch (IOException ex) { + if (ioe == null) { + ioe = ex; + } else { + ioe.addSuppressed(ex); + } + } + } + if (ioe != null) { + throw ioe; + } + } } public static class Builder { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java index 2e839e93068..3696ff0e26e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.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 @@ -29,6 +29,7 @@ import java.io.Closeable; import java.io.EOFException; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; @@ -221,9 +222,10 @@ public final class RecordingFile implements Closeable { * * @param filter filter that determines if an event should be included, not * {@code null} - * @throws IOException if an I/O error occurred, it's not a Flight - * Recorder file or a version of a JFR file that can't - * be parsed + * @throws IOException if an I/O error occurred, if {@code destination} is the + * same file as the input file for this + * {@code RecordingFile}, if it's not a Flight Recorder file + * or a version of a JFR file that can't be parsed * * @since 19 */ @@ -235,6 +237,9 @@ public final class RecordingFile implements Closeable { // package private List write(Path destination, Predicate filter, boolean collectResults) throws IOException { + if (Files.exists(destination) && Files.isSameFile(destination, file.toPath())) { + throw new IOException("Destination file can't be the same as the input file: " + destination.toAbsolutePath()); + } try (ChunkWriter cw = new ChunkWriter(file.toPath(), destination, filter, collectResults)) { try (RecordingFile rf = new RecordingFile(cw)) { while (rf.hasMoreEvents()) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java index d0d81ac63bc..b62f94d4ca3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -52,6 +52,9 @@ public final class TLSHandshakeEvent extends MirrorEvent { @Label("Cipher Suite") public String cipherSuite; + @Label("Named Group") + public String namedGroup; + @Label("Certificate Id") @Description("Peer Certificate Id") @CertificateId diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties index 62bfd36181f..547bc591bbd 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties @@ -54,15 +54,18 @@ main.command.files=\ @ Liest Optionen aus der D main.opt.footer=\nBei Optionen, die eine erfordern, ist der Wert eine durch Komma getrennte\nListe von Elementen, die jeweils eines der folgenden Formate verwenden:\n \n glob:\n regex: - - err.not.a.task=Aufgabe muss einen der folgenden Werte aufweisen: : {0} err.missing.arg=kein Wert angegeben für {0} err.ambiguous.arg=Wert für Option {0} beginnt mit "--", aber muss das Format {0}= verwenden err.not.a.dir=Kein Verzeichnis: {0} err.not.a.jimage=Keine jimage-Datei: {0} -err.invalid.jimage={0} kann nicht geöffnet werden: {1} err.no.jimage=Kein jimage angegeben err.option.unsupported={0} nicht unterstützt: {1} err.unknown.option=unbekannte Option: {0} err.cannot.create.dir=Verzeichnis {0} kann nicht erstellt werden + +# General failure to open a jimage file. +# {0} = path of jimage file, {1} = underlying error message +err.invalid.jimage={0} kann nicht geöffnet werden: {1} +# More specific alternative for cases of version mismatch +err.wrong.version={0} kann nicht geöffnet werden: Nicht übereinstimmende Datei- und Toolversion\nVerwenden Sie "/bin/jimage" für das JDK, das mit der jimage-Datei verknüpft ist:\n{1} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties index 7ab6d7f655f..461154b9cc2 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties @@ -54,15 +54,18 @@ main.command.files=\ @ ファイルからオプ main.opt.footer=\nを必要とするオプションの場合、値は、次の形式のいずれかを使用する、\n要素のカンマ区切りリストになります:\n \n glob:\n regex: - - err.not.a.task=タスクはのいずれかである必要があります: {0} err.missing.arg={0}に値が指定されていません err.ambiguous.arg=オプション{0}の値が"--"で始まっています。{0}=形式を使用する必要があります err.not.a.dir=ディレクトリではありません: {0} err.not.a.jimage=jimageファイルではありません: {0} -err.invalid.jimage={0}を開けません: {1} err.no.jimage=jimageが提供されていません err.option.unsupported={0}はサポートされていません: {1} err.unknown.option=不明なオプション: {0} err.cannot.create.dir=ディレクトリ{0}を作成できません + +# General failure to open a jimage file. +# {0} = path of jimage file, {1} = underlying error message +err.invalid.jimage={0}を開けません: {1} +# More specific alternative for cases of version mismatch +err.wrong.version={0}を開けません: ファイルとツールのバージョンが一致していません\njimageファイルに関連付けられたJDKの''/bin/jimage''を使用してください:\n{1} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties index 37954b9b743..19ca8617a0a 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties @@ -54,15 +54,18 @@ main.command.files=\ @<文件名> 从文件中 main.opt.footer=\n对于需要 的选项,值将为逗号分隔的元素列表,\n每个元素使用以下格式之一:\n \n glob:\n regex: - - err.not.a.task=任务必须是 之一:{0} err.missing.arg=没有为{0}指定值 err.ambiguous.arg=选项 {0} 的值以 "--" 开头,应使用 {0}= 格式 err.not.a.dir=不是目录:{0} err.not.a.jimage=不是 jimage 文件:{0} -err.invalid.jimage=无法打开 {0}: {1} err.no.jimage=未提供 jimage err.option.unsupported=不支持{0}: {1} err.unknown.option=未知选项: {0} err.cannot.create.dir=无法创建目录 {0} + +# General failure to open a jimage file. +# {0} = path of jimage file, {1} = underlying error message +err.invalid.jimage=无法打开 {0}: {1} +# More specific alternative for cases of version mismatch +err.wrong.version=无法打开 {0}:文件和工具版本不匹配\n请为与 jimage 文件关联的 JDK 使用 ''/bin/jimage'':\n{1} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java new file mode 100644 index 00000000000..3f663983828 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java @@ -0,0 +1,114 @@ +/* + * 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 jdk.tools.jlink.internal.plugins; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Map; + +import jdk.tools.jlink.internal.ResourcePoolEntryFactory; +import jdk.tools.jlink.plugin.PluginException; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolEntry; + +/** + * Creates the cacerts keystore in the output image with the certificates of + * the specified aliases only. + */ +public class CACertsPlugin extends AbstractPlugin { + + private static final String RES = "/java.base/lib/security/cacerts"; + + // cacerts keystore aliases + private String[] aliases; + + public CACertsPlugin() { + super("cacerts"); + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public void configure(Map config) { + String option = config.get(getName()); + if (option == null) { + throw new AssertionError(); + } + // If alias has a comma in it, this won't work, but no cacerts + // aliases have commas. + aliases = option.split(","); + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + in.transformAndCopy(res -> { + if (res.type() == ResourcePoolEntry.Type.NATIVE_LIB && + res.path().equals(RES)) { + byte[] cacerts = transformCACerts(res.content()); + return ResourcePoolEntryFactory.create(res, cacerts); + } + return res; + }, out); + return out.build(); + } + + /** + * Creates a keystore containing only the certificates of the specified + * aliases. + */ + private byte[] transformCACerts(InputStream content) { + try { + var ks = KeyStore.getInstance("PKCS12"); + ks.load(content, null); + Map certs = new HashMap<>(aliases.length); + for (var alias : aliases) { + var cert = ks.getCertificate(alias); + if (cert == null) { + throw new PluginException( + "alias " + alias + " does not exist"); + } + certs.put(alias, cert); + } + ks.load(null, null); + for (var entry : certs.entrySet()) { + ks.setCertificateEntry(entry.getKey(), entry.getValue()); + } + var baos = new ByteArrayOutputStream(); + ks.store(baos, null); + return baos.toByteArray(); + } catch (PluginException pe) { + throw pe; + } catch (Exception ex) { + throw new PluginException(ex); + } + } +} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index 7e3c26fa7b8..892ba73249e 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -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 @@ -53,6 +53,20 @@ release-info.usage=\ \ Any number of = pairs can be passed.\n\ \ del: is to delete the list of keys in release file. +cacerts.argument=[,]* + +cacerts.description=\ +Create the cacerts keystore in the output image with only the certificates\n\ +of the specified aliases. is the name of an alias in the cacerts\n\ +keystore in the java.base module. + +cacerts.usage=\ +\ --cacerts [,]*\n\ +\ Create the cacerts keystore in the output image\n\ +\ with only the certificates of the specified\n\ +\ aliases. is the name of an alias in the\n\ +\ cacerts keystore in the java.base module. + class-for-name.argument= class-for-name.description=\ diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties index a5dc70061f6..54620d1ff26 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties @@ -45,7 +45,7 @@ compress.argument=[:filter=] compress.description= リソースの圧縮に使用する圧縮。 -compress.usage=\ --compress リソースの圧縮に使用する圧縮:\n 使用可能な値は\n zip-'{0-9}'です。zip-0では圧縮は行われず、\n zip-9では最適な圧縮が行われます。\n デフォルトはzip-6です。 +compress.usage=\ --compress <圧縮> リソースの圧縮に使用する圧縮:\n 使用可能な値は\n zip-'{0-9}'です。zip-0では圧縮は行われず、\n zip-9では最適な圧縮が行われます。\n デフォルトはzip-6です。 compress.warn.argumentdeprecated=警告: --compressの{0}引数は非推奨であり、今後のリリースで削除される可能性があります diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java index ba66da53604..0adc1ce6d37 100644 --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.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 @@ -81,5 +81,6 @@ module jdk.jlink { jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin, jdk.tools.jlink.internal.plugins.VendorVersionPlugin, jdk.tools.jlink.internal.plugins.CDSPlugin, - jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin; + jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin, + jdk.tools.jlink.internal.plugins.CACertsPlugin; } diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index b95424fdde9..1ee4d08646d 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -235,6 +235,16 @@ Options Description : Generate CDS archive if the runtime image supports the CDS feature. +### Plugin `cacerts` + +Options +: `--cacerts=`*alias*\[`,`*alias*\]\* + +Description +: Create the `cacerts` keystore in the output image with only the + certificates of the specified aliases. *alias* is the name of an alias + in the `cacerts` keystore in the java.base module. + ## jlink Examples The following command creates a runtime image in the directory `greetingsapp`. diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties index 345ed36b7be..5a0d6df3950 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties @@ -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,6 +36,9 @@ resource.menu-icon=Menüsymbol resource.rpm-spec-file=RPM-Spezifikationsdatei resource.systemd-unit-file=systemd-Einheitsdatei +summary.property.linux-package-name=Packagename +summary.property.linux-required-packages-search=Suche nach erforderlichen Packages + error.tool-not-found.advice=Installieren Sie die erforderlichen Packages error.tool-old-version.advice=Installieren Sie die erforderlichen Packages @@ -49,19 +52,12 @@ error.rpm-arch-not-detected="RPM-Architektur konnte nicht erkannt werden" message.icon-not-png=Das angegebene Symbol "{0}" ist keine PNG-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet. message.test-for-tool=Test für [{0}]. Ergebnis: {1} -message.outputting-to-location=DEB für Installationsprogramm wird generiert in: {0}. -message.output-to-location=Package (.deb) gespeichert in: {0}. message.debs-like-licenses=Debian-Packages müssen eine Lizenz angeben. Bei fehlender Lizenz geben einige Linux-Distributionen eine Meldung über eine Beeinträchtigung der Anwendungsqualität aus. -message.outputting-bundle-location=RPM für Installationsprogramm wird generiert in: {0}. -message.output-bundle-location=Package (.rpm) gespeichert in: {0}. message.ldd-not-available=ldd-Befehl nicht gefunden. Packageabhängigkeiten werden nicht generiert. message.deb-ldd-not-available.advice=Installieren Sie das DEB-Package "libc-bin", um ldd abzurufen. message.rpm-ldd-not-available.advice=Installieren Sie das RPM-Package "glibc-common", um ldd abzurufen. -warning.foreign-app-image=Warnung: app-image-Verzeichnis wurde von jpackage nicht generiert. -message.not-default-bundler-no-dependencies-lookup={0} ist nicht der Standardpackagetyp. Packageabhängigkeiten werden nicht generiert. - -error.unexpected-package-property=Erwarteter Wert der Eigenschaft "{0}": [{1}]. Tatsächlicher Wert in Ausgabepackage: [{2}]. Anscheinend enthielt die benutzerdefinierte Datei "{3}" aus dem Ressourcenverzeichnis einen hartcodierten Wert der Eigenschaft "{0}" +error.unexpected-package-property=Der erwartete Wert der Eigenschaft "{0}" ist [{1}]. Der tatsächliche Wert im Ausgabepackage ist [{2}]. Anscheinend ist der Wert der Eigenschaft "{0}" in der Datei "{3}" im Ressourcenverzeichnis hartcodiert error.unexpected-package-property.advice=Verwenden Sie die Musterzeichenfolge [{0}] anstelle des hartcodierten Wertes [{1}] der {2}-Eigenschaft in der benutzerdefinierten Datei "{3}" -error.unexpected-default-package-property.advice=Legen Sie den Wert der {0}-Eigenschaft in der benutzerdefinierten Datei "{1}" nicht explizit fest +error.unexpected-default-package-property.advice=Legen Sie den Wert der Eigenschaft "{0}" in der benutzerdefinierten Datei "{1}" nicht explizit fest diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties index d0bc4f73407..472c575ba1f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties @@ -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,6 +36,9 @@ resource.menu-icon=メニュー・アイコン resource.rpm-spec-file=RPM仕様ファイル resource.systemd-unit-file=systemdユニット・ファイル +summary.property.linux-package-name=パッケージ名 +summary.property.linux-required-packages-search=必要なパッケージの検索 + error.tool-not-found.advice=必要なパッケージをインストールしてください error.tool-old-version.advice=必要なパッケージをインストールしてください @@ -49,19 +52,12 @@ error.rpm-arch-not-detected="RPM archの検出に失敗しました" message.icon-not-png=指定したアイコン"{0}"はPNGファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。 message.test-for-tool=[{0}]のテスト。結果: {1} -message.outputting-to-location=インストーラのDEBを次に生成しています: {0} -message.output-to-location=パッケージ(.deb)は次に保存されました: {0} message.debs-like-licenses=Debianパッケージではライセンスを指定する必要があります。ライセンスがない場合、一部のLinuxディストリビューションでアプリケーションの品質に問題が発生する場合があります。 -message.outputting-bundle-location=インストーラのRPMを次に生成しています: {0} -message.output-bundle-location=パッケージ(.rpm)は次に保存されました: {0} message.ldd-not-available=lddコマンドが見つかりませんでした。パッケージ依存性は生成されません。 message.deb-ldd-not-available.advice="libc-bin" DEBパッケージをインストールしてlddを取得します。 message.rpm-ldd-not-available.advice="glibc-common" RPMパッケージをインストールしてlddを取得します。 -warning.foreign-app-image=警告: app-imageディレクトリはjpackageで生成されません。 -message.not-default-bundler-no-dependencies-lookup={0}はデフォルトのパッケージ・タイプではありません。パッケージの依存性は生成されません。 - -error.unexpected-package-property="{0}"プロパティに必要な値は[{1}]です。出力パッケージの実際の値は[{2}]です。リソース・ディレクトリのカスタム"{3}"ファイルには、"{0}"プロパティのハードコードされた値が含まれているようです +error.unexpected-package-property="{0}"プロパティの予期される値は[{1}]です。出力パッケージの実際の値は[{2}]です。"{0}"プロパティの値が、リソース・ディレクトリの"{3}"ファイルにハードコードされているようです error.unexpected-package-property.advice=カスタム"{3}"ファイルで{2}プロパティのハードコードされた値[{1}]ではなく、[{0}]パターン文字列を使用してください -error.unexpected-default-package-property.advice=カスタム"{1}"ファイルで{0}プロパティの値を明示的に設定しないでください +error.unexpected-default-package-property.advice=カスタム"{1}"ファイルで"{0}"プロパティの値を明示的に設定しないでください diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties index f3d62675c4d..00efdc378af 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties @@ -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,6 +36,9 @@ resource.menu-icon=菜单图标 resource.rpm-spec-file=RPM 规范文件 resource.systemd-unit-file=systemd 单元文件 +summary.property.linux-package-name=程序包名称 +summary.property.linux-required-packages-search=所需的程序包搜索 + error.tool-not-found.advice=请安装所需的程序包 error.tool-old-version.advice=请安装所需的程序包 @@ -49,19 +52,12 @@ error.rpm-arch-not-detected="无法检测 RPM 体系结构" message.icon-not-png=指定的图标 "{0}" 不是 PNG 文件, 不会使用。将使用默认图标代替。 message.test-for-tool=[{0}] 的测试。结果: {1} -message.outputting-to-location=正在为安装程序生成 DEB, 位置: {0}。 -message.output-to-location=程序包 (.deb) 已保存到: {0}。 message.debs-like-licenses=Debian 程序包应指定许可证。缺少许可证将导致某些 Linux 分发投诉应用程序质量。 -message.outputting-bundle-location=正在为安装程序生成 RPM, 位置: {0}。 -message.output-bundle-location=程序包 (.rpm) 已保存到: {0}。 message.ldd-not-available=未找到 ldd 命令。将不生成程序包被依赖对象。 message.deb-ldd-not-available.advice=安装 "libc-bin" DEB 程序包以获取 ldd。 message.rpm-ldd-not-available.advice=安装 "glibc-common" RPM 程序包以获取 ldd。 -warning.foreign-app-image=警告:jpackage 未生成 app-image 目录。 -message.not-default-bundler-no-dependencies-lookup={0} 不是默认程序包类型。将不生成程序包被依赖对象。 - -error.unexpected-package-property="{0}" 属性的预期值为 [{1}]。输出程序包中的实际值为 [{2}]。与定制的 "{3}" 文件相似,该文件所在的资源目录中包含 "{0}" 属性的硬编码值 +error.unexpected-package-property="{0}" 属性的预期值为 [{1}]。输出程序包中的实际值为 [{2}]。"{0}" 属性的值似乎是在资源目录的 "{3}" 文件中进行了硬编码 error.unexpected-package-property.advice=在定制的 "{3}" 文件中使用 [{0}] 模式字符串,而非 {2} 属性的硬编码值 [{1}] -error.unexpected-default-package-property.advice=请勿在定制的 "{1}" 文件中显式设置 {0} 属性的值 +error.unexpected-default-package-property.advice=请勿在定制的 "{1}" 文件中显式设置 "{0}" 属性的值 diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties index 02e8c029ec6..973249c7016 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties @@ -23,17 +23,19 @@ # questions. # # -error.invalid-cfbundle-version.advice=Legen Sie einen kompatiblen Wert für "app-version" fest. Gültige Versionsnummern sind ein bis drei durch Punkte getrennte Ganzzahlen. -error.explicit-sign-no-cert=Signatur wurde explizit angefordert, doch es wurde kein Signaturzertifikat gefunden -error.explicit-sign-no-cert.advice=Geben Sie gültige Werte für mac-signing-key-user-name und mac-signing-keychain an -error.certificate.expired=Zertifikat abgelaufen {0} +error.invalid-cfbundle-version.advice=Legen Sie einen kompatiblen Wert für "app-version" fest. Die gültige Version ist eine Zeichenfolge, die aus nicht negativen, durch Punkte getrennten Ganzzahlen besteht. +error.certificate.outside-validity-period=Das Zertifikat "{0}" liegt außerhalb seines Gültigkeitszeitraums error.cert.not.found=Kein Zertifikat gefunden, das [{0}] mit Schlüsselbund [{1}] entspricht error.multiple.certs.found=Mehrere Zertifikate mit Namen [{0}] in Schlüsselbund [{1}] gefunden error.app-image.mac-sign.required=Die Option --mac-sign ist mit einem vordefinierten Anwendungsimage und Typ [app-image] erforderlich -error.tool.failed.with.output="{0}" war mit folgender Ausgabe nicht erfolgreich: error.invalid-runtime-image-missing-file=Im Laufzeitimage "{0}" fehlt die Datei "{1}" +error.invalid-app-image-runtime-image-bin-dir=Laufzeitverzeichnis {0} im vordefinierten Anwendungsimage [{1}] darf nicht den Ordner "bin" enthalten error.invalid-runtime-image-bin-dir=Laufzeitimage "{0}" darf keinen Ordner "bin" enthalten error.invalid-runtime-image-bin-dir.advice=Verwenden Sie die jlink-Option --strip-native-commands, wenn das Laufzeitimage mit Option {0} generiert wird +error.invalid-app-image-plist-file=Ungültige Datei "{0}" im vordefinierten Anwendungsimage +error.invalid-derived-bundle-identifier=Es kann keine gültige Bundle-ID von den Eingabedaten abgeleitet werden +error.invalid-derived-bundle-identifier.advice=Geben Sie die Bundle-ID mit der Option "--mac-package-identifier" an + resource.app-info-plist=Info.plist der Anwendung resource.app-runtime-info-plist=Eingebettete Info.plist von Java Runtime resource.runtime-info-plist=Info.plist von Java Runtime @@ -51,30 +53,27 @@ resource.pkg-background-image=PKG-Hintergrundbild resource.pkg-pdf=Projektdefinitionsdatei resource.launchd-plist-file=launchd-PLIST-Datei -message.bundle-name-too-long-warning={0} ist auf "{1}" gesetzt. Dies ist länger als 16 Zeichen. Kürzen Sie den Wert, um die Mac-Nutzungserfahrung zu verbessern. +summary.property.mac-bundle-identifier=CFBundleIdentifier +summary.property.mac-bundle-name=CFBundleName +summary.property.mac-sign-app-image.format={0} im Verzeichnis "{1}" signieren + +warning.bundle-name-too-long-warning=Bundle-Name "{0}" ist länger als 16 Zeichen. Kürzen Sie den Wert, um die Mac-Nutzungserfahrung zu verbessern. message.preparing-info-plist=Info.plist wird vorbereitet: {0}. message.icon-not-icns= Das angegebene Symbol "{0}" ist keine ICNS-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet. message.keychain.error=Schlüsselbundliste kann nicht abgerufen werden. -message.invalid-identifier=Ungültige Mac-Bundle-ID [{0}]. -message.invalid-identifier.advice=Geben Sie die ID mit "--mac-package-identifier" an. -message.building-dmg=DMG-Package für {0} wird erstellt. message.preparing-dmg-setup=DMG-Setup wird vorbereitet: {0}. -message.creating-dmg-file=DMG-Datei wird erstellt: {0}. -message.dmg-cannot-be-overwritten=DMG-Datei [{0}] ist vorhanden und kann nicht entfernt werden. -message.output-to-location=Ergebnis von DMG-Installationsprogramm für {0}: {1}. -message.building-pkg=PKG-Package für {0} wird erstellt. message.preparing-scripts=Packageskripte werden vorbereitet. message.preparing-distribution-dist=distribution.dist wird vorbereitet: {0}. -message.signing.pkg=Warnung: Zum Signieren von PKG müssen Sie möglicherweise mit dem Schlüsselbundverwaltungstool die Option "Immer vertrauen" für Ihr Zertifikat festlegen. message.setfile.dmg=Das Festlegen des benutzerdefinierten Symbols für die DMG-Datei wurde übersprungen, weil das Utility "SetFile" nicht gefunden wurde. Durch Installieren von Xcode mit Befehlszeilentools sollte dieses Problem behoben werden. message.codesign.failed.reason.app.content="codesign" war nicht erfolgreich, und zusätzlicher Anwendungsinhalt wurde über den Parameter "--app-content" angegeben. Wahrscheinlich hat der zusätzliche Inhalt die Integrität des Anwendungs-Bundles beeinträchtigt und den Fehler verursacht. Stellen Sie sicher, das der über den Parameter "--app-content" angegebene Inhalt nicht die Integrität des Anwendungs-Bundles beeinträchtigt, oder fügen Sie ihn im Nachverarbeitungsschritt hinzu. message.codesign.failed.reason.xcode.tools=Möglicher Grund für "codesign"-Fehler ist fehlender Xcode mit Befehlszeilen-Entwicklertools. Installieren Sie Xcode mit Befehlszeilen-Entwicklertools, und prüfen Sie, ob das Problem dadurch beseitigt wird. -message.dmg.license.button.agree=Akzeptieren +message.dmg.license.button.agree=Zustimmen message.dmg.license.button.disagree=Ablehnen message.dmg.license.button.print=Drucken -message.dmg.license.button.save=Sichern... -message.dmg.license.message=Klicken Sie in “Akzeptieren”, wenn Sie mit den Bestimmungen des Software-Lizenzvertrags einverstanden sind. Falls nicht, bitte “Ablehnen” anklicken. Sie können die Software nur installieren, wenn Sie “Akzeptieren” angeklickt haben. -warning.unsigned.app.image=Warnung: Nicht signiertes app-image wird zum Erstellen von signiertem {0} verwendet. -warning.per.user.app.image.signed=Warnung: Konfiguration der installierten Anwendung pro Benutzer wird nicht unterstützt, da "{0}" im vordefinierten signierten Anwendungsimage fehlt. -warning.non.standard.contents.sub.dir=Warnung: Der Dateiname des Verzeichnisses "{0}", das für die Option --app-content angegeben wurde, ist kein Standardunterverzeichnisname im Verzeichnis "Contents" des Anwendungs-Bundles. Möglicherweise verläuft die Codesignierung und/oder Notarisierung im Ergebnisanwendungs-Bundle nicht erfolgreich. -warning.app.content.is.not.dir=Warnung: Der Wert "{0}" der Option --app-content ist kein Verzeichnis. Möglicherweise verläuft die Codesignierung und/oder Notarisierung im Ergebnisanwendungs-Bundle nicht erfolgreich. +message.dmg.license.button.save=Speichern... +message.dmg.license.message=Wenn Sie mit den Bestimmungen dieser Lizenz einverstanden sind, wählen Sie "Zustimmen" aus, um die Software zu installieren. Wenn Sie nicht zustimmen, wählen Sie "Ablehnen" aus. +warning.unsigned.app.image=Nicht signiertes vordefiniertes Anwendungsimage mit signiertem Ausgabepackage +warning.per.user.app.image.signed=Konfiguration der installierten Anwendung pro Benutzer wird nicht unterstützt, da die Datei "{0}" im signierten vordefinierten Anwendungsimage fehlt +warning.non-standard-app-content=Der Wert der Option "--app-content" kann dazu führen, dass das Ergebnisanwendungs-Bundle nicht erfolgreich signiert und/oder notarisiert werden kann +warning.non-standard-app-content.not-dir="{0}" ist kein Verzeichnis. +warning.non-standard-app-content.non-standard-dir-name=Der Name "{0}" von Verzeichnis "{1}" ist kein Standardunterverzeichnisname im Verzeichnis "Contents" eines macOS-Bundle diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties index c9de142d796..a0698c2266f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties @@ -23,17 +23,19 @@ # questions. # # -error.invalid-cfbundle-version.advice=互換性のある'app-version'値を設定します。有効なバージョンは、ドットで区切られた1から3つの整数です。 -error.explicit-sign-no-cert=署名が明示的に要求されましたが、署名証明書が見つかりません -error.explicit-sign-no-cert.advice=有効なmac-signing-key-user-nameおよびmac-signing-keychainを指定してください -error.certificate.expired=証明書が期限切れです{0} +error.invalid-cfbundle-version.advice=互換性のある'app-version'値を設定します。有効なバージョンは、負でないピリオド区切りの整数で構成される文字列です。 +error.certificate.outside-validity-period=証明書"{0}"は有効期間の範囲外です error.cert.not.found=キーチェーン[{1}]を使用する[{0}]と一致する証明書が見つかりません error.multiple.certs.found=名前[{0}]に一致する複数の証明書がキーチェーン[{1}]で見つかりました error.app-image.mac-sign.required=--mac-signオプションは、事前定義済アプリケーション・イメージおよびタイプ[app-image]で必要です -error.tool.failed.with.output="{0}"は次の出力で失敗しました: error.invalid-runtime-image-missing-file=ランタイム・イメージ"{0}"に"{1}"ファイルがありません +error.invalid-app-image-runtime-image-bin-dir=事前定義済アプリケーション・イメージ[{1}]のランタイム・ディレクトリ{0}に"bin"フォルダを含めることはできません error.invalid-runtime-image-bin-dir=ランタイム・イメージ"{0}"に"bin"フォルダを含めることはできません error.invalid-runtime-image-bin-dir.advice={0}オプションとともに使用されるランタイム・イメージを生成する場合は、--strip-native-commands jlinkオプションを使用します +error.invalid-app-image-plist-file=事前定義済アプリケーション・イメージの"{0}"ファイルが無効です +error.invalid-derived-bundle-identifier=入力データから有効なバンドル識別子を導出できません +error.invalid-derived-bundle-identifier.advice=--mac-package-identifierオプションを使用したバンドル識別子を指定してください + resource.app-info-plist=アプリケーションのInfo.plist resource.app-runtime-info-plist=埋込みJavaランタイムのInfo.plist resource.runtime-info-plist=JavaランタイムのInfo.plist @@ -51,30 +53,27 @@ resource.pkg-background-image=pkg背景イメージ resource.pkg-pdf=プロジェクト定義ファイル resource.launchd-plist-file=launchd plistファイル -message.bundle-name-too-long-warning={0}が16文字を超える''{1}''に設定されています。Macでの操作性をより良くするために短くすることを検討してください。 +summary.property.mac-bundle-identifier=CFBundleIdentifier +summary.property.mac-bundle-name=CFBundleName +summary.property.mac-sign-app-image.format="{1}"ディレクトリの{0}に署名します + +warning.bundle-name-too-long-warning=バンドル名"{0}"が16文字を超えています。Macでの操作性をより良くするために短くすることを検討してください。 message.preparing-info-plist=Info.plistを準備しています: {0}。 message.icon-not-icns= 指定したアイコン"{0}"はICNSファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。 message.keychain.error=キーチェーン・リストを取得できません。 -message.invalid-identifier=macバンドル識別子[{0}]が無効です。 -message.invalid-identifier.advice="--mac-package-identifier"で識別子を指定してください。 -message.building-dmg={0}のDMGパッケージを作成しています message.preparing-dmg-setup=dmgの設定を準備しています: {0} -message.creating-dmg-file=DMGファイルを作成しています: {0} -message.dmg-cannot-be-overwritten=Dmgファイルは存在し[{0}]、削除できません。 -message.output-to-location={0}の結果のDMGインストーラ: {1} -message.building-pkg={0}のPKGパッケージを作成しています message.preparing-scripts=パッケージ・スクリプトを準備しています message.preparing-distribution-dist=distribution.distを準備しています: {0} -message.signing.pkg=警告: PKGへの署名の場合、「キーチェーン・アクセス」ツールを使用して証明書に「常に信頼する」を設定する必要があります。 message.setfile.dmg='SetFile'ユーティリティが見つからないため、DMGファイルでのカスタム・アイコンの設定がスキップされました。Xcodeとコマンド・ライン・ツールをインストールすると、この問題は解決されます。 message.codesign.failed.reason.app.content="codesign"が失敗したため、追加のアプリケーション・コンテンツが、"--app-content"パラメータを介して提供されました。追加のコンテンツにより、アプリケーション・バンドルの整合性が損われ、失敗の原因になった可能性があります。"--app-content"パラメータを介して提供されたコンテンツによって、アプリケーション・バンドルの整合性が損われていないことを確認するか、処理後のステップで追加してください。 message.codesign.failed.reason.xcode.tools="codesign"失敗の考えられる理由は、Xcodeとコマンドライン・デベロッパ・ツールの欠落です。Xcodeとコマンドライン・デベロッパ・ツールをインストールして、問題が解決されるかを確認してください。 -message.dmg.license.button.agree=同意します -message.dmg.license.button.disagree=同意しません -message.dmg.license.button.print=印刷する +message.dmg.license.button.agree=同意する +message.dmg.license.button.disagree=同意しない +message.dmg.license.button.print=印刷 message.dmg.license.button.save=保存... -message.dmg.license.message=本ソフトウエア使用許諾契約の条件に同意される場合には、ソフトウエアをインストールするために「同意します」を押してください。 同意されない場合には、「同意しません」を押してください。 -warning.unsigned.app.image=警告: 署名されていないapp-imageを使用して署名された{0}を作成します。 -warning.per.user.app.image.signed=警告: 事前定義済の署名付きアプリケーション・イメージに"{0}"がないため、インストール済アプリケーションのユーザーごとの構成はサポートされません。 -warning.non.standard.contents.sub.dir=警告: --app-contentオプションに指定されたディレクトリ"{0}"のファイル名が、アプリケーション・バンドルの"Contents"ディレクトリ内の標準サブディレクトリ名ではありません。結果アプリケーション・バンドルは、コード署名および/または公証に失敗することがあります。 -warning.app.content.is.not.dir=警告: --app-contentオプションの値"{0}"はディレクトリではありません。結果アプリケーション・バンドルは、コード署名または公証(あるいはその両方)に失敗することがあります。 +message.dmg.license.message=このライセンスの条件に同意する場合は、「同意する」を押してソフトウェアをインストールします。同意しない場合は、「同意しない」を押してください。 +warning.unsigned.app.image=署名なしの事前定義済アプリケーション・イメージと署名付きの出力パッケージ +warning.per.user.app.image.signed=署名付きの事前定義済アプリケーション・イメージに"{0}"ファイルがないため、インストール済アプリケーションのユーザーごとの構成はサポートされません +warning.non-standard-app-content=--app-contentオプションの値によっては、結果アプリケーション・バンドルの署名または公証(あるいはその両方)に失敗することがあります +warning.non-standard-app-content.not-dir="{0}"はディレクトリではありません +warning.non-standard-app-content.non-standard-dir-name=ディレクトリ"{1}"の名前"{0}"は、macOSバンドルの"Contents"ディレクトリ内の標準サブディレクトリ名ではありません diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties index 9a925859af5..9c51fed5eae 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties @@ -23,17 +23,19 @@ # questions. # # -error.invalid-cfbundle-version.advice=设置兼容的 'app-version' 值。有效版本包含一到三个用点分隔的整数。 -error.explicit-sign-no-cert=已明确请求签名,但找不到签名证书 -error.explicit-sign-no-cert.advice=指定有效的 mac-signing-key-user-name 和 mac-signing-keychain -error.certificate.expired=证书已到期 {0} +error.invalid-cfbundle-version.advice=设置兼容的 'app-version' 值。有效版本是由非负、以句点分隔的整数组成的字符串。 +error.certificate.outside-validity-period=证书 "{0}" 超出其有效期 error.cert.not.found=使用密钥链 [{1}] 找不到与 [{0}] 匹配的证书 error.multiple.certs.found=在密钥链 [{1}] 中找到多个与名称 [{0}] 匹配的证书 error.app-image.mac-sign.required=预定义的应用程序映像和类型 [app-image] 需要 --mac-sign 选项 -error.tool.failed.with.output="{0}" 失败,显示以下输出: error.invalid-runtime-image-missing-file=运行时映像 "{0}" 缺少 "{1}" 文件 +error.invalid-app-image-runtime-image-bin-dir=预定义的应用程序映像 [{1}] 中的运行时目录 {0} 不应包含 "bin" 文件夹 error.invalid-runtime-image-bin-dir=运行时映像 "{0}" 不应包含 "bin" 文件夹 error.invalid-runtime-image-bin-dir.advice=生成与 {0} 选项一起使用的运行时映像时,使用 --strip-native-commands jlink 选项 +error.invalid-app-image-plist-file=预定义的应用程序映像中的 "{0}" 文件无效 +error.invalid-derived-bundle-identifier=无法根据输入数据推导有效的包标识符 +error.invalid-derived-bundle-identifier.advice=使用 --mac-package-identifier 选项指定包标识符 + resource.app-info-plist=应用程序 Info.plist resource.app-runtime-info-plist=嵌入式 Java 运行时 Info.plist resource.runtime-info-plist=Java 运行时 Info.plist @@ -51,30 +53,27 @@ resource.pkg-background-image=pkg 背景图像 resource.pkg-pdf=项目定义文件 resource.launchd-plist-file=launchd plist 文件 -message.bundle-name-too-long-warning={0}已设置为 ''{1}'', 其长度超过了 16 个字符。为了获得更好的 Mac 体验, 请考虑将其缩短。 +summary.property.mac-bundle-identifier=CFBundleIdentifier +summary.property.mac-bundle-name=CFBundleName +summary.property.mac-sign-app-image.format=对 "{1}" 目录中的 {0} 签名 + +warning.bundle-name-too-long-warning=包名称 "{0}" 的长度超过了 16 个字符。为了获得更好的 Mac 体验,请考虑将其缩短。 message.preparing-info-plist=正在准备 Info.plist: {0}。 message.icon-not-icns= 指定的图标 "{0}" 不是 ICNS 文件, 不会使用。将使用默认图标代替。 message.keychain.error=无法获取密钥链列表。 -message.invalid-identifier=mac 包标识符 [{0}] 无效。 -message.invalid-identifier.advice=请使用 "--mac-package-identifier" 指定标识符。 -message.building-dmg=正在为 {0} 构建 DMG 程序包。 message.preparing-dmg-setup=正在准备 dmg 设置: {0}。 -message.creating-dmg-file=正在创建 DMG 文件: {0}。 -message.dmg-cannot-be-overwritten=Dmg 文件已存在 [{0}] 且无法删除。 -message.output-to-location=为 {0} 生成的 DMG 安装程序: {1}。 -message.building-pkg=正在为 {0} 构建 PKG 程序包。 message.preparing-scripts=正在准备程序包脚本。 message.preparing-distribution-dist=正在准备 distribution.dist: {0}。 -message.signing.pkg=警告:要对 PKG 进行签名,可能需要使用“密钥链访问”工具为证书设置“始终信任”。 message.setfile.dmg=由于未找到 'SetFile' 实用程序,跳过了针对 DMG 文件设置定制图标的操作。安装带命令行工具的 Xcode 应能解决此问题。 message.codesign.failed.reason.app.content="codesign" 失败,并通过 "--app-content" 参数提供了附加应用程序内容。可能是附加内容破坏了应用程序包的完整性,导致了故障。请确保通过 "--app-content" 参数提供的内容不会破坏应用程序包的完整性,或者在后处理步骤中添加该内容。 message.codesign.failed.reason.xcode.tools="codesign" 失败可能是因为缺少带命令行开发人员工具的 Xcode。请安装带命令行开发人员工具的 Xcode,看看是否可以解决问题。 message.dmg.license.button.agree=同意 message.dmg.license.button.disagree=不同意 message.dmg.license.button.print=打印 -message.dmg.license.button.save=存储... -message.dmg.license.message=如果您同意本许可协议的条款,请按“同意”来安装此软件。如果您不同意,请按“不同意”。 -warning.unsigned.app.image=警告:使用未签名的 app-image 生成已签名的 {0}。 -warning.per.user.app.image.signed=警告:由于预定义的已签名应用程序映像中缺少 "{0}",不支持对已安装应用程序的每用户配置提供支持。 -warning.non.standard.contents.sub.dir=警告:为 --app-content 选项指定的目录 "{0}" 的文件名不是应用程序包的 "Contents" 目录中的标准子目录名称。结果应用程序包可能会使代码签名和/或公证失败。 -warning.app.content.is.not.dir=警告:--app-content 选项的值 "{0}" 不是目录。结果应用程序包可能会使代码签名和/或公证失败。 +message.dmg.license.button.save=保存... +message.dmg.license.message=如果您同意本许可证条款,请按“同意”以安装本软件。如果您不同意,请按“不同意”。 +warning.unsigned.app.image=带已签名输出程序包的未签名的预定义应用程序映像 +warning.per.user.app.image.signed=由于签名的预定义应用程序映像中缺少 "{0}" 文件,将不支持已安装应用程序的每用户配置 +warning.non-standard-app-content=--app-content 选项的值可能会导致结果应用程序包的签名和/或公证失败 +warning.non-standard-app-content.not-dir="{0}" 不是目录 +warning.non-standard-app-content.non-standard-dir-name=目录 "{1}" 的名称 "{0}" 不是 macOS 包的 "Contents" 目录中的标准子目录名称 diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties index 5b9a5728912..d9e4dc11f0a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties @@ -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 @@ -160,8 +160,7 @@ help.option.type.mac=\ Der zu erstellende Packagetyp\n Gültige help.option.vendor=\ Anbieter der Anwendung -help.option.verbose=\ Aktiviert Ausgabe im Verbose-Modus - +help.option.verbose=\ Konfiguriert die Verbose-Ausgabe. Dabei ist "category" einer der folgenden Werte\n "all"\n "console"\n "log"\n "errors"\n "progress"\n "resources"\n "summary"\n "tools"\n "trace"\n "warnings"\n\n Gesamte Konsolenausgabe unterdrücken, Logging über System.Logger-API aktivieren:\n --verbose log\n Alle Nachrichtenkategorien in der Konsole aktivieren:\n --verbose console\n Alle Nachrichtenkategorien außer "trace" und "tools" in der Konsole aktivieren:\n --verbose console,-trace,-tools\n Die Nachrichtenkategorien "trace" und "tools" in der Konsole aktivieren:\n --verbose trace,tools\n Die Nachrichtenkategorien "trace" und "tools" in der Konsole aktivieren und\n Logging über die System.Logger-API aktivieren:\n --verbose log,trace,tools\n\n Wenn die Option ohne den Wert angegeben wird, entspricht sie\n --verbose console,-trace\n Wenn die Option nicht angegeben wird, entspricht sie\n --verbose errors,warnings\n help.option.version=\ Gibt die Produktversion an den Outputstream aus und beendet den Vorgang. help.option.win-console=\ Erstellt einen Konsolenlauncher für die Anwendung. Sollte für\n Anwendungen angegeben werden, die Konsoleninteraktionen erfordern @@ -170,17 +169,18 @@ help.option.win-dir-chooser=\ Fügt ein Dialogfeld hinzu, in dem der Be help.option.win-help-url=\ URL, unter der der Benutzer weitere Informationen oder technische Unterstützung erhält -help.option.win-menu=\ Anforderung zum Hinzufügen einer Startmenüverknüpfung für diese Anwendung +help.option.win-menu=\ Fügt eine Verknüpfung im Startmenü für diese Anwendung hinzu oder fordert diese an,\n wenn "--win-shortcut-prompt" angegeben wird help.option.win-menu-group=\ Startmenügruppe, in der diese Anwendung abgelegt wird -help.option.win-per-user-install=\ Anforderung zum Ausführen einer Installation pro Benutzer +help.option.win-per-user-install=\ Installiert die Anwendung pro Benutzer. \n Ohne diese Option wird sie pro Rechner installiert -help.option.win-shortcut=\ Anforderung zum Hinzufügen einer Desktopverknüpfung für diese Anwendung +help.option.win-shortcut=\ Fügt eine Desktopverknüpfung für diese Anwendung hinzu oder fordert diese an,\n wenn "--win-shortcut-prompt" angegeben wird -help.option.win-shortcut-prompt=\ Fügt ein Dialogfeld hinzu, in dem der Benutzer auswählen kann, ob Verknüpfungen\n vom Installationsprogramm erstellt werden. +help.option.win-shortcut-prompt=\ Fügt ein Dialogfeld hinzu, wenn mindestens "--win-menu" oder "--win-shortcut" angegeben wird,\n in dem der Benutzer auswählen kann, ob diese Verknüpfungen\n vom Installationsprogramm erstellt werden help.option.win-update-url=\ URL der verfügbaren Anwendungsaktualisierungsinformationen help.option.win-upgrade-uuid=\ UUID für Upgrades für dieses Package +help.option.win-with-ui=\ Setzt eine UI für das Installationsprogramm durch diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties index ca606dda9f8..0b3ae36be46 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties @@ -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 @@ -160,8 +160,7 @@ help.option.type.mac=\ 作成するパッケージのタイプ\n help.option.vendor=\ アプリケーションのベンダー -help.option.verbose=\ 詳細な出力を有効にします - +help.option.verbose=\ 詳細出力を構成します。"category"は次のいずれかです\n "all"\n "console"\n "log"\n "errors"\n "progress"\n "resources"\n "summary"\n "tools"\n "trace"\n "warnings"\n\n すべてのコンソール出力を抑止して、System.Logger APIを使用したロギングを有効にします:\n --verbose log\n コンソールですべてのメッセージ・カテゴリを有効にします:\n --verbose console\n コンソールで"trace"と"tools"を除くすべてのメッセージ・カテゴリを有効にします:\n --verbose console,-trace,-tools\n コンソールで"trace"と"tools"のメッセージ・カテゴリを有効にします:\n --verbose trace,tools\n コンソールで"trace"と"tools"のメッセージ・カテゴリを有効にして、\n System.Logger APIを使用したロギングを有効にします:\n --verbose log,trace,tools\n\n 値なしでオプションを指定した場合、次と等しくなります\n --verbose console,-trace\n オプションを指定しない場合、次と等しくなります\n --verbose errors,warnings\n help.option.version=\ 製品バージョンを出力ストリームに出力して終了します help.option.win-console=\ アプリケーションのコンソール・ランチャを作成します。コンソール・\n インタラクションが必要なアプリケーションに指定する必要があります @@ -170,17 +169,18 @@ help.option.win-dir-chooser=\ 製品をインストールするディ help.option.win-help-url=\ ユーザーが詳細情報または技術サポートを取得できるURL -help.option.win-menu=\ このアプリケーションのスタート・メニューのショートカットを追加するリクエスト +help.option.win-menu=\ このアプリケーションのスタート・メニュー・ショートカットを追加するか、\n --win-shortcut-promptが指定されている場合は、追加するかどうかを尋ねます help.option.win-menu-group=\ このアプリケーションを配置するスタート・メニュー・グループ -help.option.win-per-user-install=\ ユーザーごとにインストールを実行するリクエスト +help.option.win-per-user-install=\ ユーザーごとにアプリケーションをインストールします。\n このオプションがない場合、マシンごとにインストールします -help.option.win-shortcut=\ このアプリケーションのデスクトップのショートカットを追加するリクエスト +help.option.win-shortcut=\ このアプリケーションのデスクトップ・ショートカットを追加するか、\n --win-shortcut-promptが指定されている場合は、追加するかどうかを尋ねます -help.option.win-shortcut-prompt=\ ショートカットをインストーラで作成するかどうかをユーザーが\n 選択できるダイアログを追加します。 +help.option.win-shortcut-prompt=\ --win-menuまたは--win-shortcutが少なくとも1つ\n 指定されている場合、これらのショートカットをインストーラで作成するかどうかをユーザーが\n 選択できるダイアログを追加します help.option.win-update-url=\ 使用可能なアプリケーション更新情報のURL help.option.win-upgrade-uuid=\ このパッケージのアップグレードに関連付けられているUUID +help.option.win-with-ui=\ インストーラにUIを強制的に表示します diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties index ac72c67ee2f..99d1915d936 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties @@ -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 @@ -160,8 +160,7 @@ help.option.type.mac=\ 要创建的程序包的类型\n 有效 help.option.vendor=\ 应用程序的供应商 -help.option.verbose=\ 启用详细的输出 - +help.option.verbose=\ 配置详细输出。其中 "category" 为以下项之一\n "all"\n "console"\n "log"\n "errors"\n "progress"\n "resources"\n "summary"\n "tools"\n "trace"\n "warnings"\n\n 隐藏所有控制台输出,通过 System.Logger API 启用日志记录:\n --verbose log\n 在控制台中启用所有消息类别:\n --verbose console\n 在控制台中启用除 "trace" 和 "tools" 之外的所有消息类别:\n --verbose console,-trace,-tools\n 在控制台中启用 "trace" 和 "tools" 消息类别:\n --verbose trace,tools\n 在控制台中启用 "trace" 和 "tools" 消息类别,并\n 通过 System.Logger API 启用日志记录:\n --verbose log,trace,tools\n\n 如果指定了不带值的该选项,则等效于\n --verbose console,-trace\n 如果未指定该选项,则等效于\n --verbose errors,warnings\n help.option.version=\ 将产品版本输出到输出流并退出。 help.option.win-console=\ 为应用程序创建控制台启动程序,应当为\n 需要控制台交互的应用程序指定 @@ -170,17 +169,18 @@ help.option.win-dir-chooser=\ 添加一个对话框以允许用户选 help.option.win-help-url=\ 用户可以从中获取更多信息或技术支持的 URL -help.option.win-menu=\ 请求为此应用程序添加“开始”菜单快捷方式 +help.option.win-menu=\ 为此应用程序添加“开始”菜单快捷方式,或者\n 如果指定了 --win-shortcut-prompt,则请求执行此操作 help.option.win-menu-group=\ 此应用程序所在的“开始”菜单组 -help.option.win-per-user-install=\ 请求基于每个用户执行安装 +help.option.win-per-user-install=\ 按用户安装应用程序。\n 如果没有此选项,则按计算机安装 -help.option.win-shortcut=\ 请求为此应用程序添加桌面快捷方式 +help.option.win-shortcut=\ 为此应用程序添加桌面快捷方式,或者\n 如果指定了 --win-shortcut-prompt,则请求执行此操作 -help.option.win-shortcut-prompt=\ 添加一个对话框以允许用户选择是否将由安装程序\n 创建快捷方式。 +help.option.win-shortcut-prompt=\ 如果至少指定了 --win-menu 或 --win-shortcut 中的一个,则添加一个\n 对话框,使用户可以选择是否将由安装程序创建\n 这些快捷方式 help.option.win-update-url=\ 可用应用程序更新信息的 URL help.option.win-upgrade-uuid=\ 与此程序包的升级关联的 UUID +help.option.win-with-ui=\ 强制安装程序具有 UI diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties index 7816f8ee71a..f27d7fa0c01 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties @@ -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 @@ -28,14 +28,24 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=Unbekannt bundle-type.win-app=Windows-Anwendungsimage -bundle-type.win-exe=EXE-Installationsprogrammpackage -bundle-type.win-msi=MSI-Installationsprogrammpackage +bundle-type.win-exe=Windows-EXE-Installationsprogramm +bundle-type.win-msi=Windows-MSI-Installationsprogramm bundle-type.mac-app=Mac-Anwendungsimage bundle-type.mac-dmg=Mac-DMG-Package bundle-type.mac-pkg=Mac-PKG-Package bundle-type.linux-app=Linux-Anwendungsimage -bundle-type.linux-deb=DEB-Bundle -bundle-type.linux-rpm=RPM-Bundle +bundle-type.linux-deb=Linux-DEB-Package +bundle-type.linux-rpm=Linux-RPM-Package + +summary.property.operation=Vorgang +summary.property.operation.format={0} erstellen +summary.property.output-bundle=Ausgabe-Bundle +summary.property.version=Version +summary.warning=WARNING: {0} +summary.multi-line-warning=WARNING: {0}: + +summary.value.disabled=Deaktiviert +summary.value.enabled=Aktiviert resource.post-app-image-script=Auszuführendes Skript nach dem Auffüllen des Anwendungsimages @@ -43,13 +53,28 @@ message.using-default-resource=Standardpackageressource {0} {1} wird verwendet ( message.no-default-resource=Keine Standardpackageressource {0} (durch Hinzufügen von {1} zu resource-dir ist eine Anpassung möglich). message.using-custom-resource-from-file=Benutzerdefinierte Packageressource {0} wird verwendet (aus Datei {1} geladen). message.using-custom-resource=Benutzerdefinierte Packageressource {0} wird verwendet (aus {1} geladen). -message.creating-app-bundle=Anwendungspackage {0} wird in {1} erstellt -message.debug-working-directory=Arbeitsverzeichnis für Debug beibehalten: {0} -message.bundle-created={0}-Package wurde erfolgreich erstellt -message.module-version=Version "{0}" aus Modul "{1}" wird als Anwendungsversion verwendet -message.error-header={0} +message.create-package=Ausgabepackagedatei wird erstellt... +message.create-app-image=Ausgabeverzeichnis für Anwendungsimage wird erstellt... +message.package-created=Ausgabepackagedatei wurde erfolgreich erstellt +message.app-image-created=Ausgabeverzeichnis für Anwendungsimage wurde erfolgreich erstellt + +message.debug-working-directory=Arbeitsverzeichnis für Debug beibehalten: {0} + +message.module-version=Version "{0}" aus Modul "{1}" wird als Anwendungsversion verwendet +message.release-version=Version "{0}" aus "release"-Datei der vordefinierten Laufzeit wird als Packageversion verwendet +message.version-normalized=Version "{0}" wird mit Normalisierung auf das von der Plattform unterstützte Format von "{1}" verwendet + +message.error-header=Fehler: {0} message.advice-header=Empfehlung zur Behebung: {0} +message.failed-command-output-header=Befehlsausgabe: + +progress.warning-header=WARNING: {0} +progress.warning-header2=WARNING: {0}: {1} + +error.command-failed-unexpected-output=Unerwartete Ausgabe bei Ausführung des Befehls {0} +error.command-failed-unexpected-exit-code=Unerwarteter Exitcode {0} bei Ausführung des Befehls {1} +error.command-failed-timed-out=Timeout bei Befehl {0} error.version-string-empty=Version darf keine leere Zeichenfolge sein error.version-string-zero-length-component=Version [{0}] enthält eine Komponente mit Nulllänge @@ -75,13 +100,19 @@ error.parameter-not-directory=Der für Parameter {1} angegebene Wert "{0}" ist k error.parameter-not-empty-directory=Der für Parameter {1} angegebene Wert "{0}" ist kein leeres Verzeichnis oder kein vorhandener Pfad error.parameter-not-url=Der für Parameter {1} angegebene Wert "{0}" ist keine gültige URL error.parameter-not-launcher-shortcut-dir=Der für Parameter {1} angegebene Wert "{0}" ist kein gültiges Verknüpfungsstartverzeichnis +error.parameter-not-mac-bundle=Der für Parameter {1} angegebene Wert "{0}" ist kein gültiges macOS-Bundle +error.parameter-not-mac-bundle-identifier=Der für Parameter {1} angegebene Wert "{0}" ist keine gültige macOS-Bundle-ID. +error.parameter-not-mac-bundle-identifier.advice=Die Bundle-ID darf keine leere Zeichenfolge sein und nur alphanumerische Zeichen (A-Z, a-z und 0-9), Bindestriche (-) und Punkte (.) enthalten error.path-parameter-ioexception=I/O-Fehler beim Zugriff auf Pfadwert "{0}" von Parameter {1} +error.parameter-invalid-value=Ungültiger Wert "{0}" für Parameter {1} angegeben error.parameter-add-launcher-malformed=Der für Parameter {1} angegebene Wert "{0}" stimmt nicht mit dem Muster = überein error.parameter-add-launcher-not-file=Der Wert des Pfades zu einer Eigenschaftendatei "{0}", der für den zusätzlichen Launcher "{1}" bereitgestellt wird, ist kein gültiger Dateipfad error.properties-parameter-not-path=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein gültiger Pfad error.properties-parameter-not-file=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist keine Datei +error.properties-parameter-not-directory=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein Verzeichnis error.properties-parameter-not-launcher-shortcut-dir=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein gültiges Verknüpfungsstartverzeichnis +error.no-extensions-for-file-association=Für Dateiverknüpfungsnummer {0} wurden keine Erweiterungen angegeben error.no-content-types-for-file-association=Für Dateiverknüpfungsnummer {0} wurden keine MIME-Typen angegeben error.no-content-types-for-file-association.advice=Geben Sie einen MIME-Typ für Dateiverknüpfungsnummer {0} an error.too-many-content-types-for-file-association=Für Dateiverknüpfungsnummer {0} wurde mehr als ein MIME-Typ angegeben @@ -96,7 +127,11 @@ error.tool-not-found.advice=Installieren Sie "{0}" error.tool-old-version="{0}" {1} oder eine neuere Version kann nicht gefunden werden error.tool-old-version.advice=Installieren Sie "{0}" {1} oder eine neuere Version -error.jlink.failed=jlink nicht erfolgreich mit: {0} +warning.tempdir.cleanup-failed=Temporäres Verzeichnis {0} konnte nicht bereinigt werden +warning.tempdir.cleanup-file-failed=Datei "{0}" im temporären Verzeichnis konnte nicht gelöscht werden + +error.output-bundle-cannot-be-overwritten=Ausgabepackagedatei "{0}" ist vorhanden und kann nicht entfernt werden. + error.blocked.option=jlink-Option [{0}] ist in --jlink-options nicht zulässig error.no.name=Name nicht mit --name angegeben. Es kann auch kein Name aus app-image abgeleitet werden error.no.name.advice=Geben Sie den Namen mit --name an diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties index 5db5ead7577..37d043861cb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties @@ -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 @@ -28,14 +28,24 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=不明 bundle-type.win-app=Windowsアプリケーション・イメージ -bundle-type.win-exe=EXEインストーラ・パッケージ -bundle-type.win-msi=MSIインストーラ・パッケージ +bundle-type.win-exe=Windows EXEインストーラ +bundle-type.win-msi=Windows MSIインストーラ bundle-type.mac-app=Macアプリケーション・イメージ bundle-type.mac-dmg=Mac DMGパッケージ bundle-type.mac-pkg=Mac PKGパッケージ bundle-type.linux-app=Linuxアプリケーション・イメージ -bundle-type.linux-deb=DEBバンドル -bundle-type.linux-rpm=RPMバンドル +bundle-type.linux-deb=Linux DEBパッケージ +bundle-type.linux-rpm=Linux RPMパッケージ + +summary.property.operation=操作 +summary.property.operation.format={0}の作成 +summary.property.output-bundle=出力バンドル +summary.property.version=バージョン +summary.warning=WARNING: {0} +summary.multi-line-warning=WARNING: {0}: + +summary.value.disabled=無効 +summary.value.enabled=有効 resource.post-app-image-script=アプリケーション・イメージを移入した後に実行するスクリプト @@ -43,13 +53,28 @@ message.using-default-resource=デフォルトのパッケージ・リソース{ message.no-default-resource=デフォルトのパッケージ・リソース{0}なし({1}をresource-dirに追加してカスタマイズ)。 message.using-custom-resource-from-file=カスタム・パッケージ・リソース{0}の使用(ファイル{1}からロード済) message.using-custom-resource=カスタム・パッケージ・リソース{0}の使用({1}からロード済) -message.creating-app-bundle=アプリケーション・パッケージを作成しています: {1}内の{0} -message.debug-working-directory=デバッグの作業ディレクトリが保持されました: {0} -message.bundle-created={0}パッケージの作成に成功しました -message.module-version=モジュール"{1}"のバージョン"{0}"をアプリケーション・バージョンとして使用 -message.error-header={0} +message.create-package=出力パッケージ・ファイルを作成しています... +message.create-app-image=出力アプリケーション・イメージ・ディレクトリを作成しています... +message.package-created=出力パッケージ・ファイルの作成に成功しました +message.app-image-created=出力アプリケーション・イメージ・ディレクトリの作成に成功しました + +message.debug-working-directory=デバッグの作業ディレクトリが保持されました: {0} + +message.module-version=モジュール"{1}"のバージョン"{0}"をアプリケーション・バージョンとして使用 +message.release-version=事前定義済ランタイムの"release"ファイルのバージョン"{0}"をパッケージ・バージョンとして使用 +message.version-normalized="{1}"からプラットフォームでサポートされる形式に正規化されたバージョン"{0}"を使用 + +message.error-header=エラー: {0} message.advice-header=修正のアドバイス: {0} +message.failed-command-output-header=コマンド出力: + +progress.warning-header=WARNING: {0} +progress.warning-header2=WARNING: {0}: {1} + +error.command-failed-unexpected-output=コマンド{0}を実行中の予期しない出力 +error.command-failed-unexpected-exit-code=コマンド{1}を実行中の予期しない終了コード{0} +error.command-failed-timed-out=コマンド{0}がタイムアウトしました error.version-string-empty=バージョンを空の文字列にすることはできません error.version-string-zero-length-component=バージョン[{0}]に長さゼロのコンポーネントが含まれます @@ -75,13 +100,19 @@ error.parameter-not-directory=パラメータ{1}に指定された値"{0}"はデ error.parameter-not-empty-directory=パラメータ{1}に指定された値"{0}"が空のディレクトリでないか、存在しないパスです error.parameter-not-url=パラメータ{1}に指定された値"{0}"は有効なURLではありません error.parameter-not-launcher-shortcut-dir=パラメータ{1}に指定された値"{0}"は、有効なショートカット起動ディレクトリではありません +error.parameter-not-mac-bundle=パラメータ{1}に指定された値"{0}"は有効なmacOSバンドルではありません +error.parameter-not-mac-bundle-identifier=パラメータ{1}に指定された値"{0}"は有効なmacOSバンドル識別子ではありません。 +error.parameter-not-mac-bundle-identifier.advice=バンドル識別子は、英数字(A-Z、a-z、0-9)、ハイフン(-)およびピリオド(.)のみを含む空でない文字列である必要があります error.path-parameter-ioexception=パラメータ{1}のパス値"{0}"へのアクセス中にI/Oエラーが発生しました +error.parameter-invalid-value=パラメータ{1}に指定された値"{0}"は無効です error.parameter-add-launcher-malformed=パラメータ{1}に指定された値"{0}"がパターン=と一致しません error.parameter-add-launcher-not-file=追加のランチャ"{1}"に指定されたプロパティ・ファイル"{0}"へのパスの値は有効なファイル・パスではありません error.properties-parameter-not-path="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"は有効なパスではありません error.properties-parameter-not-file="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"はファイルではありません +error.properties-parameter-not-directory="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"はディレクトリではありません error.properties-parameter-not-launcher-shortcut-dir="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"は、有効なショートカット起動ディレクトリではありません +error.no-extensions-for-file-association=ファイル・アソシエーション番号{0}に拡張子が指定されませんでした error.no-content-types-for-file-association=ファイル・アソシエーション番号{0}にMIMEタイプが指定されませんでした error.no-content-types-for-file-association.advice=ファイル・アソシエーション番号{0}にMIMEタイプを指定してください error.too-many-content-types-for-file-association=ファイル・アソシエーション番号{0}に複数のMIMEタイプが指定されました @@ -96,7 +127,11 @@ error.tool-not-found.advice="{0}"をインストールしてください error.tool-old-version="{0}" {1}以降が見つかりません error.tool-old-version.advice="{0}" {1}以降をインストールしてください -error.jlink.failed=jlinkが次で失敗しました: {0} +warning.tempdir.cleanup-failed=一時ディレクトリ{0}のクリーンアップに失敗しました +warning.tempdir.cleanup-file-failed=一時ディレクトリの"{0}"ファイルの削除に失敗しました + +error.output-bundle-cannot-be-overwritten=出力パッケージ・ファイル"{0}"は存在しており、削除できません。 + error.blocked.option=jlinkオプション[{0}]は--jlink-optionsでは許可されません error.no.name=名前が--nameで指定されておらず、app-imageから推論できません error.no.name.advice=--nameで名前を指定します diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties index 23540af7db2..559a7f93d2c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties @@ -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 @@ -28,14 +28,24 @@ param.copyright.default=版权所有 (C) {0,date,YYYY} param.vendor.default=未知 bundle-type.win-app=Windows 应用程序映像 -bundle-type.win-exe=EXE 安装程序包 -bundle-type.win-msi=MSI 安装程序包 +bundle-type.win-exe=Windows EXE 安装程序 +bundle-type.win-msi=Windows MSI 安装程序 bundle-type.mac-app=Mac 应用程序映像 bundle-type.mac-dmg=Mac DMG 程序包 bundle-type.mac-pkg=Mac PKG 程序包 bundle-type.linux-app=Linux 应用程序映像 -bundle-type.linux-deb=DEB 包 -bundle-type.linux-rpm=RPM 包 +bundle-type.linux-deb=Linux DEB 程序包 +bundle-type.linux-rpm=Linux RPM 程序包 + +summary.property.operation=操作 +summary.property.operation.format=创建 {0} +summary.property.output-bundle=输出包 +summary.property.version=版本 +summary.warning=WARNING: {0} +summary.multi-line-warning=WARNING: {0}: + +summary.value.disabled=已禁用 +summary.value.enabled=已启用 resource.post-app-image-script=要在填充应用程序映像之后运行的脚本 @@ -43,13 +53,28 @@ message.using-default-resource=使用默认程序包资源 {0} {1}(将 {2} 添 message.no-default-resource=无默认程序包资源 {0}(将 {1} 添加到 resource-dir 中以进行定制)。 message.using-custom-resource-from-file=使用定制程序包资源 {0} (从文件 {1} 加载)。 message.using-custom-resource=使用定制程序包资源 {0} (从 {1} 加载)。 -message.creating-app-bundle=正在 {1} 中创建应用程序包 {0} -message.debug-working-directory=用于调试的已保留工作目录: {0} -message.bundle-created=已成功地构建 {0} 程序包 -message.module-version=正在将模块 "{1}" 中的版本 "{0}" 用作应用程序版本 -message.error-header={0} +message.create-package=正在构建输出程序包文件... +message.create-app-image=正在构建输出应用程序映像目录... +message.package-created=已成功构建输出程序包文件 +message.app-image-created=已成功构建输出应用程序映像目录 + +message.debug-working-directory=用于调试的已保留工作目录: {0} + +message.module-version=正在将模块 "{1}" 中的版本 "{0}" 用作应用程序版本 +message.release-version=将预定义运行时的 "release" 文件中的版本 "{0}" 用作程序包版本 +message.version-normalized=使用版本 "{0}",该版本已从 "{1}" 规范化为平台支持的格式 + +message.error-header=错误:{0} message.advice-header=修复建议:{0} +message.failed-command-output-header=命令输出: + +progress.warning-header=WARNING: {0} +progress.warning-header2=WARNING: {0}: {1} + +error.command-failed-unexpected-output=执行命令 {0} 时出现意外的输出 +error.command-failed-unexpected-exit-code=执行命令 {1} 时出现意外的退出代码 {0} +error.command-failed-timed-out=超时命令 {0} error.version-string-empty=版本不能为空字符串 error.version-string-zero-length-component=版本 [{0}] 包含长度为零的组件 @@ -75,13 +100,19 @@ error.parameter-not-directory=为参数 {1} 提供的值 "{0}" 不是目录 error.parameter-not-empty-directory=为参数 {1} 提供的值 "{0}" 不是空目录或是不存在的路径 error.parameter-not-url=为参数 {1} 提供的值 "{0}" 不是有效的 URL error.parameter-not-launcher-shortcut-dir=为参数 {1} 提供的值 "{0}" 不是有效的快捷方式启动目录 +error.parameter-not-mac-bundle=为参数 {1} 提供的值 "{0}" 不是有效的 macOS 包 +error.parameter-not-mac-bundle-identifier=为参数 {1} 提供的值 "{0}" 不是有效的 macOS 包标识符。 +error.parameter-not-mac-bundle-identifier.advice=包标识符必须是仅包含字母数字字符(A-Z、a-z 和 0-9)、连字符 (-) 和句点 (.) 的非空字符串 error.path-parameter-ioexception=访问参数 {1} 的路径值 "{0}" 时出现 I/O 错误 +error.parameter-invalid-value=为参数 {1} 提供的值 "{0}" 无效 error.parameter-add-launcher-malformed=为参数 {1} 提供的值 "{0}" 与模式 = 不匹配 error.parameter-add-launcher-not-file=为其他启动程序 "{1}" 提供的属性文件 "{0}" 的路径值不是有效的文件路径 error.properties-parameter-not-path=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是有效路径 error.properties-parameter-not-file=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是文件 +error.properties-parameter-not-directory=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是目录 error.properties-parameter-not-launcher-shortcut-dir=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是有效的快捷方式启动目录 +error.no-extensions-for-file-association=没有为文件关联号 {0} 指定扩展名 error.no-content-types-for-file-association=没有为文件关联号{0}指定 MIME 类型 error.no-content-types-for-file-association.advice=为文件关联号 {0} 指定 MIME 类型 error.too-many-content-types-for-file-association=为文件关联号{0}指定了多个 MIME 类型 @@ -96,7 +127,11 @@ error.tool-not-found.advice=请安装 "{0}" error.tool-old-version=找不到 "{0}" {1} 或更新版本 error.tool-old-version.advice=请安装 "{0}" {1} 或更新版本 -error.jlink.failed=jlink 失败,出现 {0} +warning.tempdir.cleanup-failed=无法清除临时目录 {0} +warning.tempdir.cleanup-file-failed=无法删除临时目录中的 "{0}" 文件 + +error.output-bundle-cannot-be-overwritten=输出程序包文件 "{0}" 已存在且无法删除。 + error.blocked.option=不允许在 --jlink-options 中使用 jlink 选项 [{0}] error.no.name=未使用 --name 指定名称,无法从 app-image 推断名称 error.no.name.advice=使用 --name 指定名称 diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java index 000db1d8a10..f818e5d966e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java @@ -1555,7 +1555,9 @@ public final class CommandOutputControl { } Optional bufferContents() { - return buf.map(ByteArrayOutputStream::toString); + return buf.map(in -> { + return in.toString(ps.charset()); + }); } static Builder build(Charset charset) { @@ -1600,7 +1602,7 @@ public final class CommandOutputControl { final PrintStream ps; if (buf.isPresent() && dumpStream != null) { - ps = new PrintStream(new TeeOutputStream(List.of(buf.get(), dumpStream)), true, dumpStream.charset()); + ps = new PrintStream(new TeeOutputStream(List.of(buf.get(), dumpStream)), true, charset); } else if (!discard) { ps = buf.map(in -> { return new PrintStream(in, false, charset); diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java index 8886afc7918..404185ce832 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java @@ -46,11 +46,11 @@ sealed interface RtfConverter { } try (InputStream fin = Files.newInputStream(path)) { - byte[] firstBits = new byte[7]; + byte[] firstBits = fin.readNBytes(Details.RTF_HEADER.length()); - if (fin.read(firstBits) == firstBits.length) { + if (Details.RTF_HEADER.length() == firstBits.length) { String header = new String(firstBits); - return "{\\rtf1\\".equals(header); + return Details.RTF_HEADER.equals(header); } } @@ -58,7 +58,7 @@ sealed interface RtfConverter { } static Optional createSimple(Path path) throws IOException { - if (isRtfFile(path)) { + if (!Files.isDirectory(path) && !isRtfFile(path)) { return Optional.of(Details.Simple.VALUE); } else { return Optional.empty(); @@ -136,5 +136,6 @@ sealed interface RtfConverter { } } + private static final String RTF_HEADER = "{\\rtf1\\"; } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties index baaba64b398..a615f87841b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties @@ -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,8 +36,12 @@ resource.launcher-as-service-wix-file=WiX-Projektdatei für Serviceinstallations resource.wix-src-conv=XSLT-Stylesheet zum Konvertieren von WiX-Quellen vom Format WiX v3 in WiX v4 resource.installer-exe=Ausführbares Installationsprogramm -error.no-wix-tools=WiX-Tools nicht gefunden. Gesucht wurden WiX v3 light.exe und candle.exe oder WiX v4/v5 wix.exe, aber keine der Dateien wurde gefunden -error.no-wix-tools.advice=Laden Sie WiX 3.0 oder höher von https://wixtoolset.org herunter, und fügen Sie es zu PATH hinzu. +summary.property.win-product-code=MSI-ProductCode +summary.property.win-upgrade-code=MSI-UpgradeCode +summary.property.win-wix-version=WiX-Toolkitversion + +error.no-wix-tools=Keine verwendbare WiX Toolset-Installation gefunden +error.no-wix-tools.advice=Installieren Sie die neueste WiX-Version v3 von https://github.com/wixtoolset/wix3/releases oder WiX v4+ von https://github.com/wixtoolset/wix/releases error.version-string-wrong-format.advice=Setzen Sie den Wert des --app-version-Parameters auf eine gültige ProductVersion des Windows-Installationsprogramms. error.msi-product-version-components=Versionszeichenfolge [{0}] muss zwischen 2 und 4 Komponenten aufweisen. error.msi-product-version-major-out-of-range=Hauptversion muss im Bereich [0, 255] liegen @@ -45,7 +49,6 @@ error.msi-product-version-build-out-of-range=Build-Teil der Version muss im Bere error.msi-product-version-minor-out-of-range=Nebenversion muss im Bereich [0, 255] liegen error.version-swap=Versionsinformationen für {0} konnten nicht aktualisiert werden error.icon-swap=Symbol für {0} konnte nicht aktualisiert werden -error.invalid-envvar=Ungültiger Wert der {0}-Umgebungsvariable error.lock-resource=Sperren nicht erfolgreich: {0} error.unlock-resource=Aufheben der Sperre nicht erfolgreich: {0} error.read-wix-l10n-file=Datei {0} konnte nicht geparst werden @@ -55,14 +58,5 @@ error.missing-service-installer=Serviceinstallationsprogramm "service-installer. error.missing-service-installer.advice=Fügen Sie das Serviceinstallationsprogramm "service-installer.exe" zum Ressourcenverzeichnis hinzu message.icon-not-ico=Das angegebene Symbol "{0}" ist keine ICO-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet. -message.potential.windows.defender.issue=Warnung: Windows Defender verhindert eventuell die korrekte Ausführung von jpackage. Wenn ein Problem auftritt, deaktivieren Sie das Echtzeitmonitoring, oder fügen Sie einen Ausschluss für das Verzeichnis "{0}" hinzu. -message.outputting-to-location=EXE für Installationsprogramm wird generiert in: {0}. -message.output-location=Installationsprogramm (.exe) gespeichert in: {0} message.tool-version=[{0}]-Version [{1}] erkannt. -message.wrong-tool-version=[{0}]-Version {1} wurde erkannt. Erforderlich ist jedoch Version {2}. -message.use-wix36-features=WiX {0} erkannt. Erweiterte Bereinigungsaktion wird aktiviert. -message.product-code=MSI-ProductCode: {0}. -message.upgrade-code=MSI-UpgradeCode: {0}. message.preparing-msi-config=MSI-Konfiguration wird vorbereitet: {0}. -message.generating-msi=MSI wird generiert: {0}. - diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties index 119c7532b1f..2cd43d436e8 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties @@ -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,8 +36,12 @@ resource.launcher-as-service-wix-file=サービス・インストーラWiXプロ resource.wix-src-conv=WiXソースをWiX v3からWiX v4フォーマットに変換するXSLTスタイルシート resource.installer-exe=インストーラ実行可能ファイル -error.no-wix-tools=WiXツールが見つかりません。WiX v3 light.exeとcandle.exeまたはWiX v4/v5 wix.exeを探しましたが、いずれも見つかりませんでした -error.no-wix-tools.advice=WiX 3.0以降をhttps://wixtoolset.orgからダウンロードし、PATHに追加します。 +summary.property.win-product-code=MSI ProductCode +summary.property.win-upgrade-code=MSI UpgradeCode +summary.property.win-wix-version=WiX Toolkitバージョン + +error.no-wix-tools=使用可能なWiX Toolsetインストールが見つかりません +error.no-wix-tools.advice=最新のWiX v3をhttps://github.com/wixtoolset/wix3/releasesからインストールするか、WiX v4+をhttps://github.com/wixtoolset/wix/releasesからインストールしてください error.version-string-wrong-format.advice=--app-versionパラメータの値を有効なWindows Installer ProductVersionに設定します。 error.msi-product-version-components=バージョン文字列[{0}]には、2から4つのコンポーネントが含まれている必要があります。 error.msi-product-version-major-out-of-range=メジャー・バージョンは範囲[0, 255]内である必要があります @@ -45,7 +49,6 @@ error.msi-product-version-build-out-of-range=バージョンのビルド部分 error.msi-product-version-minor-out-of-range=マイナー・バージョンは範囲[0, 255]内である必要があります error.version-swap={0}のバージョン情報の更新に失敗しました error.icon-swap={0}のアイコンの更新に失敗しました -error.invalid-envvar={0}環境変数の値が無効です error.lock-resource=ロックに失敗しました: {0} error.unlock-resource=ロック解除に失敗しました: {0} error.read-wix-l10n-file={0}ファイルの解析に失敗しました @@ -55,14 +58,5 @@ error.missing-service-installer=リソース・ディレクトリに'service-ins error.missing-service-installer.advice=リソース・ディレクトリに'service-installer.exe'サービス・インストーラを追加します message.icon-not-ico=指定したアイコン"{0}"はICOファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。 -message.potential.windows.defender.issue=警告: Windows Defenderが原因でjpackageが機能しないことがあります。問題が発生した場合は、リアルタイム・モニタリングを無効にするか、ディレクトリ"{0}"の除外を追加することにより、問題に対処できます。 -message.outputting-to-location=インストーラのEXEを次に生成しています: {0} -message.output-location=インストーラ(.exe)は次に保存されました: {0} message.tool-version=[{0}]バージョン[{1}]が検出されました。 -message.wrong-tool-version=[{0}]バージョン{1}が検出されましたが、バージョン{2}が必要です。 -message.use-wix36-features=WiX {0}が検出されました。拡張クリーンアップ・アクションを有効化しています。 -message.product-code=MSI ProductCode: {0}。 -message.upgrade-code=MSI UpgradeCode: {0}。 message.preparing-msi-config=MSI構成を準備しています: {0} -message.generating-msi=MSIを生成しています: {0}。 - diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties index 66d8a9d8b96..4c0c2e826ba 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties @@ -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,8 +36,12 @@ resource.launcher-as-service-wix-file=服务安装程序 WiX 项目文件 resource.wix-src-conv=将 WiX 源码从 WiX v3 格式转换为 WiX v4 格式的 XSLT 样式表 resource.installer-exe=安装程序可执行文件 -error.no-wix-tools=找不到 WiX 工具。已查找 WiX v3 light.exe 和 candle.exe 或 WiX v4/v5 wix.exe,但都未找到 -error.no-wix-tools.advice=从 https://wixtoolset.org 下载 WiX 3.0 或更高版本,然后将其添加到 PATH。 +summary.property.win-product-code=MSI ProductCode +summary.property.win-upgrade-code=MSI UpgradeCode +summary.property.win-wix-version=WiX 工具包版本 + +error.no-wix-tools=未找到可用的 WiX 工具集安装 +error.no-wix-tools.advice=从 https://github.com/wixtoolset/wix3/releases 安装最新的 WiX v3,或从 https://github.com/wixtoolset/wix/releases 安装 WiX v4+ error.version-string-wrong-format.advice=将 --app-version 参数的值设置为有效的 Windows Installer ProductVersion。 error.msi-product-version-components=版本字符串 [{0}] 必须包含 2 到 4 个组成部分。 error.msi-product-version-major-out-of-range=主版本必须位于 [0, 255] 范围中 @@ -45,7 +49,6 @@ error.msi-product-version-build-out-of-range=版本的工作版本部分必须 error.msi-product-version-minor-out-of-range=次版本必须位于 [0, 255] 范围中 error.version-swap=无法更新 {0} 的版本信息 error.icon-swap=无法更新 {0} 的图标 -error.invalid-envvar={0} 环境变量的值无效 error.lock-resource=无法锁定:{0} error.unlock-resource=无法解锁:{0} error.read-wix-l10n-file=无法解析 {0} 文件 @@ -55,14 +58,5 @@ error.missing-service-installer=在资源目录中找不到 'service-installer.e error.missing-service-installer.advice=将 'service-installer.exe' 服务安装程序添加到资源目录 message.icon-not-ico=指定的图标 "{0}" 不是 ICO 文件, 不会使用。将使用默认图标代替。 -message.potential.windows.defender.issue=警告:Windows Defender 可能会阻止 jpackage 正常工作。如果存在问题,可以通过禁用实时监视或者为目录 "{0}" 添加排除项来解决。 -message.outputting-to-location=正在为安装程序生成 EXE, 位置: {0}。 -message.output-location=安装程序 (.exe) 已保存到: {0} message.tool-version=检测到 [{0}] 版本 [{1}]。 -message.wrong-tool-version=检测到 [{0}] 版本 {1}, 但需要版本 {2}。 -message.use-wix36-features=检测到 WiX {0}。正在启用高级清除操作。 -message.product-code=MSI ProductCode:{0}。 -message.upgrade-code=MSI UpgradeCode:{0}。 message.preparing-msi-config=正在准备 MSI 配置: {0}。 -message.generating-msi=正在生成 MSI: {0}。 - diff --git a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c index 6c0554a5c32..5a01e3ad738 100644 --- a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c +++ b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, 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 @@ -151,7 +151,7 @@ Java_com_sun_management_internal_DiagnosticCommandImpl_getDiagnosticCommandInfo jobjectArray args; jobject obj; jmmOptionalSupport mos; - jint ret = jmm_interface_management_ext->GetOptionalSupport(env, &mos); + jmm_interface_management_ext->GetOptionalSupport(env, &mos); jsize num_commands; dcmdInfo* dcmd_info_array; jstring jname, jdesc, jimpact, cmd; diff --git a/test/docs/TEST.ROOT b/test/docs/TEST.ROOT index 11cba9c1c88..a42f6c99aa1 100644 --- a/test/docs/TEST.ROOT +++ b/test/docs/TEST.ROOT @@ -38,7 +38,7 @@ groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library diff --git a/test/hotspot/gtest/aarch64/aarch64-asmtest.py b/test/hotspot/gtest/aarch64/aarch64-asmtest.py index bcf786d6f1f..b5386d47a73 100644 --- a/test/hotspot/gtest/aarch64/aarch64-asmtest.py +++ b/test/hotspot/gtest/aarch64/aarch64-asmtest.py @@ -1121,7 +1121,7 @@ class SVEVectorOp(Instruction): self._bitwiseop = False if name[0] == 'f': self._width = RegVariant(2, 3) - elif not self._isPredicated and (name in ["and", "eor", "orr", "bic", "eor3"]): + elif not self._isPredicated and (name in ["and", "bic", "bsl", "eor", "eor3", "orr"]): self._width = RegVariant(3, 3) self._bitwiseop = True elif name == "revb": @@ -1150,7 +1150,7 @@ class SVEVectorOp(Instruction): width + [str(self.reg[i]) for i in range(1, self.numRegs)])) def astr(self): - firstArg = 0 if self._name == "eor3" else 1 + firstArg = 0 if self._name in ["bsl", "eor3"] else 1 formatStr = "%s%s" + ''.join([", %s" for i in range(firstArg, self.numRegs)]) if self._dnm == 'dn': formatStr += ", %s" @@ -2163,6 +2163,10 @@ generate(SpecialCases, [["ccmn", "__ ccmn(zr, zr, 3u, Assembler::LE);", # SVE2 instructions ["histcnt", "__ sve_histcnt(z16, __ S, p0, z16, z16);", "histcnt\tz16.s, p0/z, z16.s, z16.s"], ["histcnt", "__ sve_histcnt(z17, __ D, p0, z17, z17);", "histcnt\tz17.d, p0/z, z17.d, z17.d"], + ["umullb", "__ sve_umullb(z16, __ H, z17, z18);", "umullb\tz16.h, z17.b, z18.b"], + ["umullt", "__ sve_umullt(z19, __ S, z20, z21);", "umullt\tz19.s, z20.h, z21.h"], + ["smullb", "__ sve_smullb(z22, __ D, z23, z24);", "smullb\tz22.d, z23.s, z24.s"], + ["smullt", "__ sve_smullt(z25, __ H, z26, z27);", "smullt\tz25.h, z26.b, z27.b"], ]) print "\n// FloatImmediateOp" @@ -2258,6 +2262,7 @@ generate(SVEVectorOp, [["add", "ZZZ"], # SVE2 instructions ["bext", "ZZZ"], ["bdep", "ZZZ"], + ["bsl", "ZZZ"], ["eor3", "ZZZ"], ["sqadd", "ZPZ", "m", "dn"], ["sqsub", "ZPZ", "m", "dn"], diff --git a/test/hotspot/gtest/aarch64/asmtest.out.h b/test/hotspot/gtest/aarch64/asmtest.out.h index cd9fd4cfe9a..95832a1faf6 100644 --- a/test/hotspot/gtest/aarch64/asmtest.out.h +++ b/test/hotspot/gtest/aarch64/asmtest.out.h @@ -1180,6 +1180,10 @@ __ sve_splice(z0, __ D, p0, z1); // splice z0.d, p0, z0.d, z1.d __ sve_histcnt(z16, __ S, p0, z16, z16); // histcnt z16.s, p0/z, z16.s, z16.s __ sve_histcnt(z17, __ D, p0, z17, z17); // histcnt z17.d, p0/z, z17.d, z17.d + __ sve_umullb(z16, __ H, z17, z18); // umullb z16.h, z17.b, z18.b + __ sve_umullt(z19, __ S, z20, z21); // umullt z19.s, z20.h, z21.h + __ sve_smullb(z22, __ D, z23, z24); // smullb z22.d, z23.s, z24.s + __ sve_smullt(z25, __ H, z26, z27); // smullt z25.h, z26.b, z27.b // FloatImmediateOp __ fmovd(v0, 2.0); // fmov d0, #2.0 @@ -1419,38 +1423,39 @@ __ sve_fabd(z14, __ S, p5, z22); // fabd z14.s, p5/m, z14.s, z22.s __ sve_bext(z5, __ H, z18, z0); // bext z5.h, z18.h, z0.h __ sve_bdep(z9, __ D, z2, z3); // bdep z9.d, z2.d, z3.d - __ sve_eor3(z14, z4, z29); // eor3 z14.d, z14.d, z4.d, z29.d - __ sve_sqadd(z14, __ D, p5, z4); // sqadd z14.d, p5/m, z14.d, z4.d - __ sve_sqsub(z27, __ S, p3, z22); // sqsub z27.s, p3/m, z27.s, z22.s - __ sve_uqadd(z31, __ S, p6, z11); // uqadd z31.s, p6/m, z31.s, z11.s - __ sve_uqsub(z12, __ B, p4, z28); // uqsub z12.b, p4/m, z12.b, z28.b + __ sve_bsl(z14, z4, z29); // bsl z14.d, z14.d, z4.d, z29.d + __ sve_eor3(z14, z22, z4); // eor3 z14.d, z14.d, z22.d, z4.d + __ sve_sqadd(z27, __ S, p3, z22); // sqadd z27.s, p3/m, z27.s, z22.s + __ sve_sqsub(z31, __ S, p6, z11); // sqsub z31.s, p6/m, z31.s, z11.s + __ sve_uqadd(z12, __ B, p4, z28); // uqadd z12.b, p4/m, z12.b, z28.b + __ sve_uqsub(z28, __ D, p4, z4); // uqsub z28.d, p4/m, z28.d, z4.d // SVEReductionOp - __ sve_andv(v28, __ D, p4, z4); // andv d28, p4, z4.d - __ sve_orv(v6, __ S, p0, z15); // orv s6, p0, z15.s - __ sve_eorv(v1, __ S, p5, z18); // eorv s1, p5, z18.s - __ sve_smaxv(v2, __ H, p2, z4); // smaxv h2, p2, z4.h - __ sve_sminv(v11, __ S, p2, z28); // sminv s11, p2, z28.s - __ sve_umaxv(v3, __ H, p5, z31); // umaxv h3, p5, z31.h - __ sve_uminv(v24, __ H, p5, z15); // uminv h24, p5, z15.h - __ sve_fminv(v6, __ S, p3, z8); // fminv s6, p3, z8.s - __ sve_fmaxv(v21, __ D, p7, z4); // fmaxv d21, p7, z4.d - __ sve_fadda(v24, __ S, p5, z6); // fadda s24, p5, s24, z6.s - __ sve_uaddv(v4, __ D, p2, z9); // uaddv d4, p2, z9.d + __ sve_andv(v6, __ S, p0, z15); // andv s6, p0, z15.s + __ sve_orv(v1, __ S, p5, z18); // orv s1, p5, z18.s + __ sve_eorv(v2, __ H, p2, z4); // eorv h2, p2, z4.h + __ sve_smaxv(v11, __ S, p2, z28); // smaxv s11, p2, z28.s + __ sve_sminv(v3, __ H, p5, z31); // sminv h3, p5, z31.h + __ sve_umaxv(v24, __ H, p5, z15); // umaxv h24, p5, z15.h + __ sve_uminv(v6, __ H, p3, z8); // uminv h6, p3, z8.h + __ sve_fminv(v21, __ D, p7, z4); // fminv d21, p7, z4.d + __ sve_fmaxv(v24, __ S, p5, z6); // fmaxv s24, p5, z6.s + __ sve_fadda(v4, __ D, p2, z9); // fadda d4, p2, d4, z9.d + __ sve_uaddv(v10, __ S, p1, z31); // uaddv d10, p1, z31.s // AddWideNEONOp - __ saddwv(v10, v11, __ T8H, v12, __ T8B); // saddw v10.8H, v11.8H, v12.8B - __ saddwv2(v5, v6, __ T8H, v7, __ T16B); // saddw2 v5.8H, v6.8H, v7.16B - __ saddwv(v31, v0, __ T4S, v1, __ T4H); // saddw v31.4S, v0.4S, v1.4H - __ saddwv2(v22, v23, __ T4S, v24, __ T8H); // saddw2 v22.4S, v23.4S, v24.8H - __ saddwv(v25, v26, __ T2D, v27, __ T2S); // saddw v25.2D, v26.2D, v27.2S - __ saddwv2(v15, v16, __ T2D, v17, __ T4S); // saddw2 v15.2D, v16.2D, v17.4S - __ uaddwv(v3, v4, __ T8H, v5, __ T8B); // uaddw v3.8H, v4.8H, v5.8B - __ uaddwv2(v18, v19, __ T8H, v20, __ T16B); // uaddw2 v18.8H, v19.8H, v20.16B - __ uaddwv(v14, v15, __ T4S, v16, __ T4H); // uaddw v14.4S, v15.4S, v16.4H - __ uaddwv2(v10, v11, __ T4S, v12, __ T8H); // uaddw2 v10.4S, v11.4S, v12.8H - __ uaddwv(v2, v3, __ T2D, v4, __ T2S); // uaddw v2.2D, v3.2D, v4.2S - __ uaddwv2(v10, v11, __ T2D, v12, __ T4S); // uaddw2 v10.2D, v11.2D, v12.4S + __ saddwv(v25, v26, __ T8H, v27, __ T8B); // saddw v25.8H, v26.8H, v27.8B + __ saddwv2(v15, v16, __ T8H, v17, __ T16B); // saddw2 v15.8H, v16.8H, v17.16B + __ saddwv(v3, v4, __ T4S, v5, __ T4H); // saddw v3.4S, v4.4S, v5.4H + __ saddwv2(v18, v19, __ T4S, v20, __ T8H); // saddw2 v18.4S, v19.4S, v20.8H + __ saddwv(v14, v15, __ T2D, v16, __ T2S); // saddw v14.2D, v15.2D, v16.2S + __ saddwv2(v10, v11, __ T2D, v12, __ T4S); // saddw2 v10.2D, v11.2D, v12.4S + __ uaddwv(v2, v3, __ T8H, v4, __ T8B); // uaddw v2.8H, v3.8H, v4.8B + __ uaddwv2(v10, v11, __ T8H, v12, __ T16B); // uaddw2 v10.8H, v11.8H, v12.16B + __ uaddwv(v8, v9, __ T4S, v10, __ T4H); // uaddw v8.4S, v9.4S, v10.4H + __ uaddwv2(v11, v12, __ T4S, v13, __ T8H); // uaddw2 v11.4S, v12.4S, v13.8H + __ uaddwv(v22, v23, __ T2D, v24, __ T2S); // uaddw v22.2D, v23.2D, v24.2S + __ uaddwv2(v3, v4, __ T2D, v5, __ T4S); // uaddw2 v3.2D, v4.2D, v5.4S __ bind(forth); @@ -1469,30 +1474,30 @@ 0x9101a1a0, 0xb10a5cc8, 0xd10810aa, 0xf10fd061, 0x120cb166, 0x321764bc, 0x52174681, 0x720c0227, 0x9241018e, 0xb25a2969, 0xd278b411, 0xf26aad01, - 0x14000000, 0x17ffffd7, 0x140004cb, 0x94000000, - 0x97ffffd4, 0x940004c8, 0x3400000a, 0x34fffa2a, - 0x340098aa, 0x35000008, 0x35fff9c8, 0x35009848, - 0xb400000b, 0xb4fff96b, 0xb40097eb, 0xb500001d, - 0xb5fff91d, 0xb500979d, 0x10000013, 0x10fff8b3, - 0x10009733, 0x90000013, 0x36300016, 0x3637f836, - 0x363096b6, 0x3758000c, 0x375ff7cc, 0x3758964c, + 0x14000000, 0x17ffffd7, 0x140004d0, 0x94000000, + 0x97ffffd4, 0x940004cd, 0x3400000a, 0x34fffa2a, + 0x3400994a, 0x35000008, 0x35fff9c8, 0x350098e8, + 0xb400000b, 0xb4fff96b, 0xb400988b, 0xb500001d, + 0xb5fff91d, 0xb500983d, 0x10000013, 0x10fff8b3, + 0x100097d3, 0x90000013, 0x36300016, 0x3637f836, + 0x36309756, 0x3758000c, 0x375ff7cc, 0x375896ec, 0x128313a0, 0x528a32c7, 0x7289173b, 0x92ab3acc, 0xd2a0bf94, 0xf2c285e8, 0x9358722f, 0x330e652f, 0x53067f3b, 0x93577c53, 0xb34a1aac, 0xd35a4016, 0x13946c63, 0x93c3dbc8, 0x54000000, 0x54fff5a0, - 0x54009420, 0x54000001, 0x54fff541, 0x540093c1, - 0x54000002, 0x54fff4e2, 0x54009362, 0x54000002, - 0x54fff482, 0x54009302, 0x54000003, 0x54fff423, - 0x540092a3, 0x54000003, 0x54fff3c3, 0x54009243, - 0x54000004, 0x54fff364, 0x540091e4, 0x54000005, - 0x54fff305, 0x54009185, 0x54000006, 0x54fff2a6, - 0x54009126, 0x54000007, 0x54fff247, 0x540090c7, - 0x54000008, 0x54fff1e8, 0x54009068, 0x54000009, - 0x54fff189, 0x54009009, 0x5400000a, 0x54fff12a, - 0x54008faa, 0x5400000b, 0x54fff0cb, 0x54008f4b, - 0x5400000c, 0x54fff06c, 0x54008eec, 0x5400000d, - 0x54fff00d, 0x54008e8d, 0x5400000e, 0x54ffefae, - 0x54008e2e, 0x5400000f, 0x54ffef4f, 0x54008dcf, + 0x540094c0, 0x54000001, 0x54fff541, 0x54009461, + 0x54000002, 0x54fff4e2, 0x54009402, 0x54000002, + 0x54fff482, 0x540093a2, 0x54000003, 0x54fff423, + 0x54009343, 0x54000003, 0x54fff3c3, 0x540092e3, + 0x54000004, 0x54fff364, 0x54009284, 0x54000005, + 0x54fff305, 0x54009225, 0x54000006, 0x54fff2a6, + 0x540091c6, 0x54000007, 0x54fff247, 0x54009167, + 0x54000008, 0x54fff1e8, 0x54009108, 0x54000009, + 0x54fff189, 0x540090a9, 0x5400000a, 0x54fff12a, + 0x5400904a, 0x5400000b, 0x54fff0cb, 0x54008feb, + 0x5400000c, 0x54fff06c, 0x54008f8c, 0x5400000d, + 0x54fff00d, 0x54008f2d, 0x5400000e, 0x54ffefae, + 0x54008ece, 0x5400000f, 0x54ffef4f, 0x54008e6f, 0xd40658e1, 0xd4014d22, 0xd4046543, 0xd4273f60, 0xd44cad80, 0xd503201f, 0xd503203f, 0xd503205f, 0xd503209f, 0xd50320bf, 0xd503219f, 0xd50323bf, @@ -1535,7 +1540,7 @@ 0x39598921, 0x795d3077, 0x399d0675, 0x7998d8f3, 0x79dbd02a, 0xb99d068a, 0xfd5d11a0, 0xbd58d76b, 0xfd1ac72d, 0xbd1d9c14, 0x5800001a, 0x18ffda33, - 0xf8991100, 0xd8007880, 0xf8a758e0, 0xf9989d80, + 0xf8991100, 0xd8007920, 0xf8a758e0, 0xf9989d80, 0x1a0b0298, 0x3a1c01a0, 0x5a0400ea, 0x7a02020f, 0x9a1d028c, 0xba0e01ad, 0xda140186, 0xfa19022c, 0x0b2b877e, 0x2b21c8ee, 0xcb3ba47d, 0x6b3ae9a0, @@ -1718,7 +1723,8 @@ 0x0420bc31, 0x05271e11, 0x6545e891, 0x6585e891, 0x65c5e891, 0x6545c891, 0x6585c891, 0x65c5c891, 0x052c8020, 0x056c8020, 0x05ac8020, 0x05ec8020, - 0x45b0c210, 0x45f1c231, 0x1e601000, 0x1e603000, + 0x45b0c210, 0x45f1c231, 0x45527a30, 0x45957e93, + 0x45d872f6, 0x455b7759, 0x1e601000, 0x1e603000, 0x1e621000, 0x1e623000, 0x1e641000, 0x1e643000, 0x1e661000, 0x1e663000, 0x1e681000, 0x1e683000, 0x1e6a1000, 0x1e6a3000, 0x1e6c1000, 0x1e6c3000, @@ -1769,13 +1775,13 @@ 0x65b45aff, 0x65e07fa2, 0x04454097, 0x044d6e3c, 0x04283148, 0x04bd3013, 0x047731b0, 0x04ed33d7, 0x05606ad9, 0x056b6fd9, 0x658896ce, 0x4540b245, - 0x45c3b449, 0x04243bae, 0x44d8948e, 0x449a8edb, - 0x4499997f, 0x441b938c, 0x04da309c, 0x049821e6, - 0x04993641, 0x04482882, 0x048a2b8b, 0x044937e3, - 0x044b35f8, 0x65872d06, 0x65c63c95, 0x659834d8, - 0x04c12924, 0x0e2c116a, 0x4e2710c5, 0x0e61101f, - 0x4e7812f6, 0x0ebb1359, 0x4eb1120f, 0x2e251083, - 0x6e341272, 0x2e7011ee, 0x6e6c116a, 0x2ea41062, - 0x6eac116a, + 0x45c3b449, 0x04243fae, 0x0436388e, 0x44988edb, + 0x449a997f, 0x4419938c, 0x44db909c, 0x049a21e6, + 0x04983641, 0x04592882, 0x04882b8b, 0x044a37e3, + 0x044935f8, 0x044b2d06, 0x65c73c95, 0x658634d8, + 0x65d82924, 0x048127ea, 0x0e3b1359, 0x4e31120f, + 0x0e651083, 0x4e741272, 0x0eb011ee, 0x4eac116a, + 0x2e241062, 0x6e2c116a, 0x2e6a1128, 0x6e6d118b, + 0x2eb812f6, 0x6ea51083, }; // END Generated code -- do not edit diff --git a/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp b/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp index a3d856de0df..db240aeee90 100644 --- a/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp +++ b/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp @@ -30,9 +30,15 @@ #include "asm/macroAssembler.hpp" #include "compiler/disassembler.hpp" #include "memory/resourceArea.hpp" +#include "runtime/threadWXSetters.inline.hpp" +#include "utilities/powerOfTwo.hpp" #include "nativeInst_aarch64.hpp" #include "unittest.hpp" +// remove comment for debug log +//#define LOG_PLEASE +#include "testutils.hpp" + #define __ _masm. static void asm_check(const unsigned int *insns, const unsigned int *insns1, size_t len) { @@ -488,4 +494,160 @@ TEST_VM(AssemblerAArch64, merge_ldst_after_expand) { asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]); } +TEST_VM(AssemblerAArch64, native_instruction_load_predicates) { + static uint32_t insns[] = { + 0x58000000, // ldr x0, #0 + 0x18000000, // ldr w0, #0 + 0x1C000000, // ldr s0, #0 (VR bit set to 1, enabling SIMD/FP register) + }; + + NativeInstruction* ni_ldr = nativeInstruction_at(&insns[0]); + EXPECT_TRUE(ni_ldr->is_load_literal()); + EXPECT_TRUE(ni_ldr->is_ldr_gpr_literal()); + EXPECT_FALSE(ni_ldr->is_ldrw_gpr_literal()); + + NativeInstruction* ni_ldrw = nativeInstruction_at(&insns[1]); + EXPECT_TRUE(ni_ldrw->is_load_literal()); + EXPECT_FALSE(ni_ldrw->is_ldr_gpr_literal()); + EXPECT_TRUE(ni_ldrw->is_ldrw_gpr_literal()); + + NativeInstruction* ni_ldrs = nativeInstruction_at(&insns[2]); + EXPECT_TRUE(ni_ldrs->is_load_literal()); + EXPECT_FALSE(ni_ldrs->is_ldr_gpr_literal()); + EXPECT_FALSE(ni_ldrs->is_ldrw_gpr_literal()); +} + +struct GtestFriendToMacroAssembler { + + typedef MacroAssembler::KlassDecodeMode Mode; + + typedef address (*decode_function)(narrowKlass encoded); + typedef narrowKlass (*encode_function)(address decoded); + + using CKP = CompressedKlassPointers; + using MA = MacroAssembler; + + static void build_and_run_encode_decode_klass(address base, int shift, + Mode expected_mode) { + + if ((shift + CKP::narrow_klass_pointer_bits()) > 32) { + return; // unsupported + } + + LOG_HERE("base " PTR_FORMAT " shift %d => mode %d: ", + p2u(base), shift, (int)expected_mode); + + // Test if the given base+shift value (with an assumed maximum Klass* range) + // yields the expected decode mode + const Mode real_mode = MA::klass_decode_mode(base, shift, CKP::max_klass_range_size()); + + ASSERT_EQ(real_mode, expected_mode) << " different mode?"; + + // Now generate encode and decode functions for this base and shift ... + BufferBlob* bb = BufferBlob::create("test_decode_klass", 512); + CodeBuffer code(bb); + address entry_encode = nullptr; + address entry_decode = nullptr; + + { + MA masm(&code); + + entry_encode = masm.pc(); + masm.emit_encode_klass_not_null(c_rarg0, // x0: dst+return + c_rarg0, // x0: src + rscratch1, // x8: tmp + base, shift, + real_mode); + masm.ret(lr); + + entry_decode = masm.pc(); + masm.emit_decode_klass_not_null(c_rarg0, // x0: dst+return + c_rarg0, // x0: src + rscratch1, // x8: tmp + base, shift, + real_mode); + masm.ret(lr); + + masm.flush(); // icache invalidate + } + + { + MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXExec, Thread::current())); + + // ... and call it with some values spread over the full width of the narrowKlass range. + const narrowKlass highest = right_n_bits(CKP::narrow_klass_pointer_bits()); + + const struct { narrowKlass encoded; address decoded; } testvalues [] = { + { 0, base }, + // The highest value we can express with the current narrowKlass width + { highest, (address)(p2u(base) + ((uint64_t)highest << shift)) }, + // midpoint + { highest / 2, (address)(p2u(base) + (((uint64_t)highest / 2) << shift)) } + }; + constexpr int num_testvalues = sizeof(testvalues) / sizeof(testvalues[0]); + + for (int i = 0; i < num_testvalues; i++) { + const narrowKlass encoded = testvalues[i].encoded; + const address decoded = testvalues[i].decoded; + + const narrowKlass encoded_real = ((encode_function)entry_encode)(decoded); + LOG_HERE(" encode: " PTR_FORMAT " => " UINT32_FORMAT_X, p2u(decoded), encoded_real); + EXPECT_EQ(encoded_real, encoded) << " bad encode?"; + + const address decoded_real = ((decode_function)entry_decode)(encoded); + LOG_HERE(" decode: " UINT32_FORMAT_X " => " PTR_FORMAT, encoded, p2u(decoded_real)); + EXPECT_EQ(decoded_real, decoded) << " bad decode?"; + } + } + BufferBlob::free(bb); + } + + static void test_decode_encode_klass() { + + for (int shift = 0; shift < CKP::max_shift(); shift++) { + + // test zero-based + build_and_run_encode_decode_klass((address)nullptr, shift, MA::KlassDecodeZero); + + // test XOR-based encoding + // Base must be a valid immediate that does not intersect the highest left-shifted nKlass + const int lowest_xor_base_bit = 32; + const int highest_xor_base_bit = 51; // highest user address space bit on all our platforms + + // Highest base bit set + build_and_run_encode_decode_klass((address)nth_bit(highest_xor_base_bit), shift, MA::KlassDecodeXor); + // lowest base bit set + build_and_run_encode_decode_klass((address)nth_bit(lowest_xor_base_bit), shift, MA::KlassDecodeXor); + // all base bits set + build_and_run_encode_decode_klass((address)(right_n_bits(highest_xor_base_bit - lowest_xor_base_bit) << lowest_xor_base_bit), + shift, MA::KlassDecodeXor); + + // test movk-based + // Only bits in the third quadrant and not a valid immediate + build_and_run_encode_decode_klass((address)0x0000'A000'0000'0000ULL, 0, MA::KlassDecodeMovk); + + // test Fallback mode. + // base has low bits that intersect with nKlass, no other mode would work + build_and_run_encode_decode_klass((address)(0x5'0000'0000ULL + os::vm_page_size()), + shift, MA::KlassDecodeFallback); + build_and_run_encode_decode_klass((address)(0x5'0000'0000ULL - os::vm_page_size()), + shift, MA::KlassDecodeFallback); + + // a base that has ones in all four quadrants to trigger the full movz+3*movk path + // when loading the immediate + build_and_run_encode_decode_klass((address)right_n_bits(52), + shift, MA::KlassDecodeFallback); + + // spread over multiple 16-bit quadrants and not encodable as immediate, + // no other mode would work + build_and_run_encode_decode_klass((address)0x00AA'AAA0'0000'0000ULL, + shift, MA::KlassDecodeFallback); + } + } +}; + +// Run this with and without UseCompactObjectHeaders +TEST_VM(AssemblerAArch64, decode_encode_klass_not_null) { + GtestFriendToMacroAssembler::test_decode_encode_klass(); +} #endif // AARCH64 diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp index af066657377..a4169ff6ba6 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp @@ -26,6 +26,9 @@ #include "gc/shared/gc_globals.hpp" #include "gc/shenandoah/shenandoahAllocRate.inline.hpp" +#include "gc/shenandoah/shenandoahStripedCounter.inline.hpp" +#include "runtime/atomic.hpp" +#include "threadHelper.inline.hpp" class ShenandoahMockClock { public: @@ -120,6 +123,131 @@ TEST_VM_F(ShenandoahAllocationRateTest, accelerated_consumption_momentary_spike) EXPECT_EQ(consumption.accelerated_consumption(), 0UL); } +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_single_dominant_allocator) { + // Single mutator: one stripe allocates, other stripes stay empty. + ShenandoahStripedCounter stripes; + if (stripes.num_stripes() == 1) { + // Regression requires multiple stripes. + return; + } + + ShenandoahAllocRate rate(MINIMUM_SAMPLE_SIZE, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + // Multiple epochs prove the allocation-path trigger re-fires without force_update(). + constexpr size_t alloc_size = 64; + constexpr size_t epochs = 4; + for (size_t allocated = 0; allocated < MINIMUM_SAMPLE_SIZE * epochs; allocated += alloc_size) { + allocate(rate, alloc_size); + } + + // Old one-shot trigger left the average at zero until force_update(). + EXPECT_GT(rate.weighted_average(), 0.0); +} + +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_rearms_when_floor_lowered) { + // Lowering the floor must re-arm a stripe that crossed the old share. + constexpr size_t high_floor = 1 * M; + constexpr size_t low_floor = 1024; + constexpr size_t alloc_size = 64; + + ShenandoahAllocRate rate(high_floor, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + + // Accumulate below the high floor, but above the later lowered share. + constexpr size_t phase1_bytes = high_floor / 4; + for (size_t allocated = 0; allocated < phase1_bytes; allocated += alloc_size) { + allocate(rate, alloc_size); + } + EXPECT_DOUBLE_EQ(rate.weighted_average(), 0.0); // nothing drained yet + + // A GC lowers the floor. + rate.set_minimum_sample_size(low_floor); + + // New crossings under the lowered floor must sample without force_update(). + for (size_t allocated = 0; allocated < low_floor * 16; allocated += alloc_size) { + allocate(rate, alloc_size); + } + + EXPECT_GT(rate.weighted_average(), 0.0); +} + +// Concurrent multi-threaded sampling. Many threads drive allocated() past the aggregate floor at +// the same time, so distinct JavaThreads spread across stripes and stay hot simultaneously. This is +// the regime the sampling guard is written for: contended try_lock (multiple threads cross their +// per-stripe share at once, only one wins the lock), multi-stripe sum() aggregation (the floor is +// reached by several occupied stripes, not one), and the drain-race clause (one thread's add() +// captures a stripe value that another thread drains before the first takes the lock). +class ConcurrentAllocators { +public: + static constexpr int kThreads = 8; + static constexpr size_t kPerThreadEpochs = 500; + static constexpr size_t kAllocSize = 64; + // Every thread allocates this many bytes; the grand total spans many minimum-sample-size epochs. + static constexpr size_t kPerThreadBytes = MINIMUM_SAMPLE_SIZE * kPerThreadEpochs; +}; + +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_concurrent_allocators) { + ShenandoahAllocRate rate(MINIMUM_SAMPLE_SIZE, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + + auto worker = [&](Thread*, int) { + for (size_t allocated = 0; allocated < ConcurrentAllocators::kPerThreadBytes; + allocated += ConcurrentAllocators::kAllocSize) { + rate.allocated(ConcurrentAllocators::kAllocSize); + } + }; + TestThreadGroup ttg(worker, ConcurrentAllocators::kThreads); + ttg.doit(); + ttg.join(); + + // No force_update() was called: every sample came from the contended allocation path. Across + // thousands of epochs driven by all threads, sampling must have fired and drained repeatedly. + EXPECT_GT(rate.weighted_average(), 0.0); +} + +// Concurrent skew: a few threads hold their stripes just below the per-stripe share and keep them +// hot (spinning at the barrier), while a heavy thread pushes the aggregate over the floor. The +// sample can then only be taken because sum() aggregates the heavy stripe with the held stripes -- +// exercising the multi-stripe floor crossing, not a single dominant stripe. +class ConcurrentSkew { +public: + static constexpr int kHolderThreads = 6; + static constexpr size_t kHeavyEpochs = 300; + static constexpr size_t kAllocSize = 64; +}; + +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_concurrent_skew) { + ShenandoahStripedCounter stripes; + if (stripes.num_stripes() == 1) { + // A multi-stripe aggregate crossing is only meaningful with more than one stripe. + return; + } + + ShenandoahAllocRate rate(MINIMUM_SAMPLE_SIZE, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + + // Each holder adds just under the per-stripe share once, then stays live for the whole run, so + // several stripes remain simultaneously occupied below their individual share. Their adds never + // cross a share alone, but they contend on the counter and feed sum(). + Atomic stop(false); + const size_t per_stripe_share = MINIMUM_SAMPLE_SIZE / stripes.num_stripes(); + const size_t holder_target = per_stripe_share > 2 ? per_stripe_share - 1 : 1; + auto holder = [&](Thread*, int) { + rate.allocated(holder_target); + while (!stop.load_relaxed()) { /* keep the thread (and its stripe) live */ } + }; + TestThreadGroup holders(holder, ConcurrentSkew::kHolderThreads); + holders.doit(); + + // Heavy stream on the main thread's own stripe. Its crossings, added to the held stripes, take + // sum() over the floor; the re-armed trigger must sample every epoch off the allocation path. + const size_t heavy_bytes = MINIMUM_SAMPLE_SIZE * ConcurrentSkew::kHeavyEpochs; + for (size_t allocated = 0; allocated < heavy_bytes; allocated += ConcurrentSkew::kAllocSize) { + allocate(rate, ConcurrentSkew::kAllocSize); + } + + stop.store_relaxed(true); + holders.join(); + + EXPECT_GT(rate.weighted_average(), 0.0); +} + TEST_VM_F(ShenandoahAllocationRateTest, accelerated_consumption_accelerating) { ShenandoahAllocRate rate(256, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); for (uint i = 0; i < BASELINE_SAMPLES; ++i) { diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp index 4633d8588d3..483464fba17 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp @@ -55,7 +55,7 @@ protected: old = new ShenandoahOldGeneration(8); old->set_promoted_reserve(512 * HeapWordSize); - old->expend_promoted(256 * HeapWordSize); + old->try_expend_promoted(256 * HeapWordSize); old->set_evacuation_reserve(512 * HeapWordSize); Thread* thread = Thread::current(); @@ -171,10 +171,10 @@ TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserv EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations"; } -TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted_should_increase_expended) { +TEST_VM_F(ShenandoahOldGenerationTest, test_try_expend_promoted_should_increase_expended) { SKIP_IF_NOT_SHENANDOAH(); size_t expended_before = old->get_promoted_expended(); - old->expend_promoted(128); + EXPECT_TRUE(old->try_expend_promoted(128)) << "Should fit within reserve"; size_t expended_after = old->get_promoted_expended(); EXPECT_EQ(expended_before + 128, expended_after) << "Should expend promotion"; } diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp new file mode 100644 index 00000000000..db4933a9264 --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp @@ -0,0 +1,118 @@ +/* + * 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 "gc/shenandoah/shenandoahStripedCounter.inline.hpp" +#include "runtime/atomic.hpp" +#include "threadHelper.inline.hpp" +#include "unittest.hpp" + +// Single thread: every add() maps to the same stripe, so add() returns the running total and +// sum()/drain() are exact. +TEST_VM(ShenandoahStripedCounter, single_thread_exact) { + ShenandoahStripedCounter c; + size_t expected = 0; + for (size_t i = 1; i <= 1000; i++) { + const size_t got = c.add(i); + expected += i; + // A lone writer owns one stripe, so its stripe total is the whole total. + EXPECT_EQ(got, expected); + EXPECT_EQ(c.sum(), expected); + } + // drain() returns everything and resets to zero; a second drain sees nothing. + EXPECT_EQ(c.drain(), expected); + EXPECT_EQ(c.sum(), (size_t) 0); + EXPECT_EQ(c.drain(), (size_t) 0); +} + +// Draining mid-stream starts a fresh epoch, and sum()/drain() stay exact across the boundary. +TEST_VM(ShenandoahStripedCounter, drain_epochs) { + ShenandoahStripedCounter c; + size_t expected = 0; + for (size_t i = 0; i < 500; i++) { + c.add(7); + expected += 7; + } + EXPECT_EQ(c.sum(), expected); + // Drain (starts a new epoch), then keep adding. + EXPECT_EQ(c.drain(), expected); + expected = 0; + for (size_t i = 0; i < 500; i++) { + c.add(13); + expected += 13; + } + EXPECT_EQ(c.sum(), expected); + EXPECT_EQ(c.drain(), expected); +} + +// Multi-threaded stress. N threads each add a fixed number of bytes; when quiescent, sum() must +// equal the grand total, and the periodic-drain variant must lose nothing (every byte lands in +// exactly one drain or the final sum). Distinct JavaThreads make current_stripe() actually spread +// writers across stripes. +class StripedCounterStress { +public: + static constexpr int kThreads = 8; + static constexpr size_t kPerThreadAdds = 20000; + static constexpr size_t kBytesPerAdd = 8; + static constexpr size_t kGrandTotal = (size_t) kThreads * kPerThreadAdds * kBytesPerAdd; +}; + +TEST_VM(ShenandoahStripedCounter, mt_quiescent_sum_exact) { + ShenandoahStripedCounter c; + auto worker = [&](Thread*, int) { + for (size_t i = 0; i < StripedCounterStress::kPerThreadAdds; i++) { + c.add(StripedCounterStress::kBytesPerAdd); + } + }; + TestThreadGroup ttg(worker, StripedCounterStress::kThreads); + ttg.doit(); + ttg.join(); + // All writers quiesced: sum() is now exact and must account for every byte. + EXPECT_EQ(c.sum(), StripedCounterStress::kGrandTotal); + EXPECT_EQ(c.drain(), StripedCounterStress::kGrandTotal); + EXPECT_EQ(c.sum(), (size_t) 0); +} + +TEST_VM(ShenandoahStripedCounter, mt_concurrent_drain_loses_nothing) { + ShenandoahStripedCounter c; + Atomic drained(0); + Atomic done(0); + auto worker = [&](Thread*, int) { + for (size_t i = 0; i < StripedCounterStress::kPerThreadAdds; i++) { + c.add(StripedCounterStress::kBytesPerAdd); + } + done.add_then_fetch(1); + }; + TestThreadGroup ttg(worker, StripedCounterStress::kThreads); + ttg.doit(); + // Drain concurrently with the adds; each drain moves bytes to a new epoch without losing them. + while (done.load_relaxed() < StripedCounterStress::kThreads) { + drained.add_then_fetch(c.drain()); + } + ttg.join(); + // Final drain sweeps up whatever raced the last concurrent drain. + drained.add_then_fetch(c.drain()); + // Every byte added landed in exactly one drain. + EXPECT_EQ(drained.load_relaxed(), StripedCounterStress::kGrandTotal); + EXPECT_EQ(c.sum(), (size_t) 0); +} diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 641edaba4da..6f1dedf6923 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -22,6 +22,7 @@ * */ +#include "opto/intrinsicnode.hpp" #include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "runtime/os.hpp" @@ -225,12 +226,6 @@ const TypeIntMirror* TypeIntMirror::operator->() const { return this; } -template -bool TypeIntMirror::contains(U u) const { - S s = S(u); - return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); -} - template bool TypeIntMirror::contains(const TypeIntMirror& o) const { return TypeIntHelper::int_type_is_subset(*this, o); @@ -745,9 +740,31 @@ public: } }; +template +class OpCompressBits { +public: + U operator()(U v1, U v2) const { + constexpr int W = HotSpotNumerics::type_width(); + if constexpr (W == 64) { + return CompressBitsNode::compress_bits(v1, v2, W); + } else { + return U(uint(CompressBitsNode::compress_bits(uint(v1), uint(v2), W))); + } + } +}; + +template +class InferCompressBits { +public: + CTP operator()(CTP t1, CTP t2) const { + return RangeInference::infer_compress_bits(t1, t2); + } +}; + TEST(opto, range_inference) { test_binary(); test_binary(); test_binary(); + test_binary(); test_lshift(); } diff --git a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp index 5d475d2f955..fcc1354c773 100644 --- a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp +++ b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp @@ -58,16 +58,15 @@ public: address i_addr = (address)&i; bool found_i_addr = false; - // stack grows downward + // Stack grows downward. address stack_top = stack_end + stack_size; - bool found_stack_top = false; { MemTracker::NmtVirtualMemoryLocker vml; + // For thread stacks, this historically named API visits resident ranges. + // Not all committed pages have to be resident. VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rgn_found, [&](const VirtualMemoryRegion& rgn) { - if (rgn.base() + rgn.size() == stack_top) { - EXPECT_TRUE(rgn.size() <= stack_size); - found_stack_top = true; - } + EXPECT_GE(rgn.base(), stack_end); + EXPECT_LE(rgn.end(), stack_top); if (i_addr < stack_top && i_addr >= rgn.base()) { found_i_addr = true; } @@ -76,10 +75,9 @@ public: }); } - // stack and guard pages may be contiguous as one region + // Stack and guard pages may be contiguous as one region. ASSERT_TRUE(i >= 1); ASSERT_TRUE(found_i_addr); - ASSERT_TRUE(found_stack_top); } static const int PAGE_CONTAINED_IN_RANGE_TAG = -1; diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index cc059a93199..72e1080f099 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -1062,17 +1062,26 @@ TEST_VM(os, is_first_C_frame) { TEST_VM(os, trim_native_heap) { EXPECT_TRUE(os::can_trim_native_heap()); os::size_change_t sc; - sc.before = sc.after = (size_t)-1; - EXPECT_TRUE(os::trim_native_heap(&sc)); - tty->print_cr("%zu->%zu", sc.before, sc.after); - // Regardless of whether we freed memory, both before and after - // should be somewhat believable numbers (RSS). - const size_t min = 5 * M; - const size_t max = LP64_ONLY(20 * G) NOT_LP64(3 * G); - ASSERT_LE(min, sc.before); - ASSERT_GT(max, sc.before); - ASSERT_LE(min, sc.after); - ASSERT_GT(max, sc.after); + os::Linux::accurate_meminfo_t info1; + os::Linux::accurate_meminfo_t info2; + bool have_info1 = os::Linux::query_accurate_process_memory_info(&info1); + EXPECT_TRUE(os::trim_native_heap(nullptr)); + bool have_info2 = os::Linux::query_accurate_process_memory_info(&info2); + + if (have_info1 && have_info2) { + sc.before = (info1.rss + info1.swap) * K; + sc.after = (info2.rss + info2.swap) * K; + tty->print_cr("%zu->%zu", sc.before, sc.after); + + // Regardless of whether we freed memory, both before and after + // should be somewhat believable numbers (RSS). + const size_t min = 5 * M; + const size_t max = LP64_ONLY(20 * G) NOT_LP64(3 * G); + ASSERT_LE(min, sc.before); + ASSERT_GT(max, sc.before); + ASSERT_LE(min, sc.after); + ASSERT_GT(max, sc.after); + } // Should also work EXPECT_TRUE(os::trim_native_heap()); } @@ -1105,7 +1114,7 @@ TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_largepages) { const size_t lpsz = os::large_page_size(); char* p1 = os::reserve_memory_aligned(lpsz, lpsz, mtTest); ASSERT_NE(p1, nullptr); - char* p2 = os::reserve_memory_special(lpsz, lpsz, lpsz, p1, false); + char* p2 = os::reserve_memory_special(lpsz, lpsz, lpsz, p1, mtTest, false); ASSERT_EQ(p2, nullptr); // should have failed os::release_memory(p1, M); } else { diff --git a/test/hotspot/gtest/runtime/test_os_linux.cpp b/test/hotspot/gtest/runtime/test_os_linux.cpp index 9c624267c18..1a1a9f34d08 100644 --- a/test/hotspot/gtest/runtime/test_os_linux.cpp +++ b/test/hotspot/gtest/runtime/test_os_linux.cpp @@ -54,7 +54,7 @@ namespace { const size_t _size; public: static char* reserve_memory_special_huge_tlbfs(size_t bytes, size_t alignment, size_t page_size, char* req_addr, bool exec) { - return os::reserve_memory_special(bytes, alignment, page_size, req_addr, exec); + return os::reserve_memory_special(bytes, alignment, page_size, req_addr, mtTest, exec); } HugeTlbfsMemory(char* const ptr, size_t size) : _ptr(ptr), _size(size) { } ~HugeTlbfsMemory() { @@ -224,7 +224,7 @@ class TestReserveMemorySpecial : AllStatic { if (!using_explicit_hugepages()) { return; } - char* addr = os::reserve_memory_special(size, alignment, page_size, nullptr, false); + char* addr = os::reserve_memory_special(size, alignment, page_size, nullptr, mtTest, false); if (addr != nullptr) { small_page_write(addr, size); os::release_memory(addr, size); @@ -281,7 +281,7 @@ class TestReserveMemorySpecial : AllStatic { for (int i = 0; i < num_sizes; i++) { const size_t size = sizes[i]; for (size_t alignment = ag; is_aligned(size, alignment); alignment *= 2) { - char* p = os::reserve_memory_special(size, alignment, lp, nullptr, false); + char* p = os::reserve_memory_special(size, alignment, lp, nullptr, mtTest, false); if (p != nullptr) { EXPECT_TRUE(is_aligned(p, alignment)); small_page_write(p, size); @@ -296,7 +296,7 @@ class TestReserveMemorySpecial : AllStatic { for (size_t alignment = ag; is_aligned(size, alignment); alignment *= 2) { // req_addr must be at least large page aligned. char* const req_addr = align_up(mapping1, MAX2(alignment, lp)); - char* p = os::reserve_memory_special(size, alignment, lp, req_addr, false); + char* p = os::reserve_memory_special(size, alignment, lp, req_addr, mtTest, false); if (p != nullptr) { EXPECT_EQ(p, req_addr); small_page_write(p, size); @@ -311,7 +311,7 @@ class TestReserveMemorySpecial : AllStatic { for (size_t alignment = ag; is_aligned(size, alignment); alignment *= 2) { // req_addr must be at least large page aligned. char* const req_addr = align_up(mapping2, MAX2(alignment, lp)); - char* p = os::reserve_memory_special(size, alignment, lp, req_addr, false); + char* p = os::reserve_memory_special(size, alignment, lp, req_addr, mtTest, false); // as the area around req_addr contains already existing mappings, the API should always // return nullptr (as per contract, it cannot return another address) EXPECT_TRUE(p == nullptr); diff --git a/test/hotspot/gtest/runtime/test_os_windows.cpp b/test/hotspot/gtest/runtime/test_os_windows.cpp index 7541caae286..5efa0580eda 100644 --- a/test/hotspot/gtest/runtime/test_os_windows.cpp +++ b/test/hotspot/gtest/runtime/test_os_windows.cpp @@ -32,6 +32,8 @@ #include "concurrentTestRunner.inline.hpp" #include "unittest.hpp" +#include + namespace { class MemoryReleaser { char* const _ptr; @@ -67,7 +69,7 @@ void TestReserveMemorySpecial_test() { FLAG_SET_CMDLINE(UseNUMAInterleaving, false); const size_t large_allocation_size = os::large_page_size() * 4; - char* result = os::reserve_memory_special(large_allocation_size, os::large_page_size(), os::large_page_size(), nullptr, false); + char* result = os::reserve_memory_special(large_allocation_size, os::large_page_size(), os::large_page_size(), nullptr, mtTest, false); if (result == nullptr) { // failed to allocate memory, skipping the test return; @@ -77,12 +79,12 @@ void TestReserveMemorySpecial_test() { // Reserve another page within the recently allocated memory area. This should fail const size_t expected_allocation_size = os::large_page_size(); char* expected_location = result + os::large_page_size(); - char* actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), os::large_page_size(), expected_location, false); + char* actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), os::large_page_size(), expected_location, mtTest, false); EXPECT_TRUE(actual_location == nullptr) << "Should not be allowed to reserve within present reservation"; // Instead try reserving after the first reservation. expected_location = result + large_allocation_size; - actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), os::large_page_size(), expected_location, false); + actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), os::large_page_size(), expected_location, mtTest, false); EXPECT_TRUE(actual_location != nullptr) << "Unexpected reservation failure, can't verify correct location"; EXPECT_TRUE(actual_location == expected_location) << "Reservation must be at requested location"; MemoryReleaser m2(actual_location, os::large_page_size()); @@ -90,7 +92,7 @@ void TestReserveMemorySpecial_test() { // Now try to do a reservation with a larger alignment. const size_t alignment = os::large_page_size() * 2; const size_t new_large_size = alignment * 4; - char* aligned_request = os::reserve_memory_special(new_large_size, alignment, os::large_page_size(), nullptr, false); + char* aligned_request = os::reserve_memory_special(new_large_size, alignment, os::large_page_size(), nullptr, mtTest, false); EXPECT_TRUE(aligned_request != nullptr) << "Unexpected reservation failure, can't verify correct alignment"; EXPECT_TRUE(is_aligned(aligned_request, alignment)) << "Returned address must be aligned"; MemoryReleaser m3(aligned_request, new_large_size); @@ -873,4 +875,242 @@ TEST_VM(os_windows, SafeFetch32_with_page_guard_protection) { ::VirtualFree(p, 0, MEM_RELEASE); } +#define SKIP_IF_PLACEHOLDER_NOT_SUPPORTED \ + if (os::win32::VirtualAlloc2 == nullptr) GTEST_SKIP() << "VirtualAlloc2 not available"; + +TEST_VM(os, placeholder_reserve_and_convert) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + char* reserved = os::win32::convert_to_reserved(region); + ASSERT_EQ(reserved, region.base()); + + // Commit, but bypass NMT + ASSERT_NE(::VirtualAlloc(reserved, size, MEM_COMMIT, PAGE_READWRITE), nullptr); + // Touch the memory to confirm it's usable. + memset(reserved, 0xAB, size); + EXPECT_EQ((unsigned char)reserved[0], 0xAB); + EXPECT_EQ((unsigned char)reserved[size - 1], 0xAB); + + ASSERT_TRUE(::VirtualFree(reserved, 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_split_two_way) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t granularity = os::vm_allocation_granularity(); + const size_t total = 4 * granularity; + const size_t split_offset = 3 * granularity; + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(total, nullptr); + ASSERT_FALSE(region.is_empty()); + + char* original_base = region.base(); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(region, split_offset); + + // Leading piece: [base, base+split_offset) + ASSERT_EQ(split.left.base(), original_base); + ASSERT_EQ(split.left.size(), split_offset); + + // Trailing piece: [base+split_offset, base+total) + ASSERT_EQ(split.right.base(), original_base + split_offset); + ASSERT_EQ(split.right.size(), total - split_offset); + + // Convert both and commit. + char* addr1 = os::win32::convert_to_reserved(split.left); + char* addr2 = os::win32::convert_to_reserved(split.right); + ASSERT_EQ(addr1, original_base); + ASSERT_EQ(addr2, original_base + split_offset); + + // Commit, but bypass NMT + ASSERT_NE(::VirtualAlloc(addr1, split_offset, MEM_COMMIT, PAGE_READWRITE), nullptr); + ASSERT_NE(::VirtualAlloc(addr2, total - split_offset, MEM_COMMIT, PAGE_READWRITE), nullptr); + + // Touch the memory to confirm it's usable. + memset(addr1, 0x11, split_offset); + memset(addr2, 0x22, total - split_offset); + EXPECT_EQ((unsigned char)addr1[0], 0x11); + EXPECT_EQ((unsigned char)addr2[0], 0x22); + + // Verify we can release the parts separately. + ASSERT_TRUE(::VirtualFree(addr1, 0, MEM_RELEASE)); + ASSERT_TRUE(::VirtualFree(addr2, 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_split_consumes_full_range) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t region_size = os::vm_allocation_granularity(); + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(region_size, nullptr); + ASSERT_FALSE(region.is_empty()); + + char* original_base = region.base(); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(region, region_size); + + // Leading piece + ASSERT_EQ(split.left.base(), original_base); + ASSERT_EQ(split.left.size(), region_size); + + // Trailing piece + ASSERT_TRUE(split.right.is_empty()); + + // Commit and touch to confirm it's usable. + char* addr = os::win32::convert_to_reserved(split.left); + ASSERT_NE(::VirtualAlloc(addr, region_size, MEM_COMMIT, PAGE_READWRITE), nullptr); + memset(addr, 0x11, region_size); + EXPECT_EQ((unsigned char)addr[0], 0x11); + + ASSERT_TRUE(::VirtualFree(addr, 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_split_consumes_nothing) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t region_size = os::vm_allocation_granularity(); + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(region_size, nullptr); + ASSERT_FALSE(region.is_empty()); + + char* original_base = region.base(); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(region, 0); + + // Leading piece + ASSERT_TRUE(split.left.is_empty()); + + // Trailing piece + ASSERT_EQ(split.right.base(), original_base); + ASSERT_EQ(split.right.size(), region_size); + + // Commit and touch to confirm it's usable. + char* addr = os::win32::convert_to_reserved(split.right); + ASSERT_NE(::VirtualAlloc(addr, region_size, MEM_COMMIT, PAGE_READWRITE), nullptr); + memset(addr, 0x11, region_size); + EXPECT_EQ((unsigned char)addr[0], 0x11); + + ASSERT_TRUE(::VirtualFree(addr, 0, MEM_RELEASE)); +} + +TEST_VM_FATAL_ERROR_MSG(os, placeholder_double_convert, ".*Failed to convert placeholder.*") { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + // Double convert + char* reserved = os::win32::convert_to_reserved(region); + ASSERT_EQ(reserved, region.base()); + // This second conversion attempt should crash producing the error "...Failed to convert placeholder..." + reserved = os::win32::convert_to_reserved(region); +} + +TEST_VM(os, placeholder_commit_before_convert) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + // Committing should fail here, but not crash. + ASSERT_FALSE(::VirtualAlloc(region.base(), size, MEM_COMMIT, PAGE_READWRITE)); + ASSERT_TRUE(::VirtualFree(region.base(), 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_release_before_convert) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + ASSERT_TRUE(::VirtualFree(region.base(), 0, MEM_RELEASE)); +} + +// Test that reserve_with_numa_placeholder works correctly. +// On NUMA systems with a single NUMA node, there is no true interleaving +// (all chunks are put on node 0) but the placeholder split/replace path +// is still properly exercised. +TEST_VM(os_windows, placeholder_numa_reserve_commit) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t num_nodes = os::numa_get_groups_num(); + + // Enable NUMA interleaving for this test so the correct code path is taken. + AutoSaveRestore FLAG_GUARD(UseNUMAInterleaving); + AutoSaveRestore FLAG_GUARD(UseLargePages); + FLAG_SET_CMDLINE(UseNUMAInterleaving, true); + FLAG_SET_CMDLINE(UseLargePages, false); + + // Allocate a region large enough to span multiple NUMA interleave chunks. + // NUMAInterleaveGranularity defaults to 2MB + const size_t chunk_size = NUMAInterleaveGranularity; + const size_t num_chunks = 4; + const size_t size = num_chunks * chunk_size; + + char* result = os::attempt_reserve_memory_at(nullptr, size, mtTest); + ASSERT_TRUE(result != nullptr) << "Failed to reserve memory"; + ASSERT_TRUE(is_aligned(result, os::vm_allocation_granularity())); + ASSERT_TRUE(os::commit_memory(result, size, false)); + + // Walk (and touch) the chunks using the same alignment logic as reserve_with_numa_placeholder: + // the first chunk may be shorter (up to the next chunk_size boundary), + // then full chunk_size pieces, with a possible shorter trailing chunk. + PSAPI_WORKING_SET_EX_INFORMATION wsi[num_chunks + 1]; + memset(wsi, 0, sizeof(wsi)); + size_t bytes_remaining = size; + char* addr = result; + size_t actual_chunks = 0; + + while (bytes_remaining > 0) { + size_t this_chunk_size = MIN2(bytes_remaining, chunk_size - ((size_t)addr % chunk_size)); + + memset(addr, 0xDA, this_chunk_size); + + wsi[actual_chunks] = {0}; + wsi[actual_chunks].VirtualAddress = addr; + actual_chunks++; + + bytes_remaining -= this_chunk_size; + addr += this_chunk_size; + } + + BOOL query_ok = QueryWorkingSetEx(GetCurrentProcess(), wsi, sizeof(wsi)); + ASSERT_TRUE(query_ok) << "QueryWorkingSetEx failed: " << GetLastError(); + + // Verify all pages are valid (in the working set). + for (size_t i = 0; i < actual_chunks; i++) { + EXPECT_TRUE(wsi[i].VirtualAttributes.Valid) << "Chunk " << i << " page not valid in working set"; + } + + if (num_nodes > 1) { + // On a multi-NUMA system, verify that not all chunks are assigned to the same node. + ULONG first_node = (ULONG)wsi[0].VirtualAttributes.Node; + bool found_different_node = false; + for (size_t i = 1; i < actual_chunks; i++) { + if (wsi[i].VirtualAttributes.Valid && + (ULONG)wsi[i].VirtualAttributes.Node != first_node) { + found_different_node = true; + break; + } + } + EXPECT_TRUE(found_different_node) + << "All " << actual_chunks << " chunks assigned to NUMA node " << first_node + << "; expected interleaving across " << num_nodes << " nodes"; + } + + os::release_memory(result, size); +} + #endif diff --git a/test/hotspot/gtest/s390/test_assembler_s390.cpp b/test/hotspot/gtest/s390/test_assembler_s390.cpp new file mode 100644 index 00000000000..2e677508a8d --- /dev/null +++ b/test/hotspot/gtest/s390/test_assembler_s390.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, IBM Corporation. and/or its affiliates. All rights reserved. + * 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. + */ + +#if defined(S390) && !defined(ZERO) + +#include "asm/assembler.hpp" +#include "asm/assembler.inline.hpp" +#include "unittest.hpp" + +// --------------------------------------------------------------------------- +// Tests for Assembler::is_z_illtrap +// +// The three emitter forms and what they write into memory (big-endian): +// +// z_illtrap() -> 0x00 0x00 (id == 0) +// z_illtrap(int id) -> 0x00 (e.g. 0x00 0xba) +// z_illtrap_eyecatcher(...) -> ends with z_illtrap(xpattern) -> 0x00 +// +// All forms share: high byte (first byte in memory) == 0x00. +// is_z_illtrap must recognise all of them, not just 0x0000. +// --------------------------------------------------------------------------- + +TEST(AssemblerS390, is_z_illtrap_no_id) { + // z_illtrap() emits 0x0000 — must be detected. + uint8_t buf[] = { 0x00, 0x00 }; + EXPECT_TRUE(Assembler::is_z_illtrap((address)buf)) + << "z_illtrap() (0x0000) must be recognised as illtrap"; +} + +TEST(AssemblerS390, is_z_illtrap_with_id) { + // z_illtrap(id) emits 0x00 — must also be detected. + // Tests a representative set of ids actually used in the source. + const uint8_t ids[] = { 0x22, 0x55, 0x66, 0x99, 0xba, 0xd1, 0xd2, 0xee }; + for (uint8_t id : ids) { + uint8_t buf[] = { 0x00, id }; + EXPECT_TRUE(Assembler::is_z_illtrap((address)buf)) + << "z_illtrap(0x" << std::hex << (int)id << ") must be recognised as illtrap"; + } +} + +TEST(AssemblerS390, is_z_illtrap_false_positive) { + // A non-zero high byte must NOT be recognised as an illtrap. + uint8_t buf[] = { 0x07, 0x00 }; // BCR 0,0 (a NOP — not an illtrap) + EXPECT_FALSE(Assembler::is_z_illtrap((address)buf)) + << "BCR 0,0 (0x0700) must not be recognised as illtrap"; +} + +#endif // S390 && !ZERO + diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 705cded007a..14601f2ba6f 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -29,6 +29,10 @@ serviceability/AsyncGetCallTrace/MyPackage/ASGCTBaseTest.java 8308026 generic-al serviceability/jvmti/Heap/IterateHeapWithEscapeAnalysisEnabled.java 8264699 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java 8308367 generic-all +vmTestbase/nsk/jvmti/unit/functions/Dispose/JvmtiTest/TestDescription.java 8387429 generic-all +vmTestbase/nsk/jvmti/scenarios/capability/CM02/cm02t001/TestDescription.java 8299217 generic-all + + #### ## Classes not unloaded as expected (TODO, need to check if FJ keeps a reference) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index d41f1e3818d..71e626e5b39 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -58,21 +58,18 @@ compiler/codecache/jmx/PoolsIndependenceTest.java 8264632 macosx-all compiler/vectorapi/VectorRebracket128Test.java 8330538 generic-all -compiler/vectorization/TestVectorAlgorithms.java#noSuperWord 8376803 aix-ppc64,linux-s390x -compiler/vectorization/TestVectorAlgorithms.java#vanilla 8376803 aix-ppc64,linux-s390x -compiler/vectorization/TestVectorAlgorithms.java#noOptimizeFill 8376803 aix-ppc64,linux-s390x - compiler/floatingpoint/TestSubnormalFloat.java 8317810 generic-i586 compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586 compiler/codecache/CodeCacheFullCountTest.java 8332954 generic-all compiler/interpreter/Test6833129.java 8335266 generic-i586 -compiler/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.java 8286300 linux-s390x compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 -compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all +compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java 8387392 windows-aarch64 + +compiler/vectorapi/VectorStoreMaskIdentityTest.java 8388281 generic-all ############################################################################# @@ -86,10 +83,10 @@ gc/TestAllocHumongousFragment.java#aggressive 8298781 generic-all gc/TestAllocHumongousFragment.java#g1 8298781 generic-all gc/TestAllocHumongousFragment.java#static 8298781 generic-all gc/shenandoah/oom/TestAllocOutOfMemory.java#large 8344312 linux-ppc64le -gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#generational 8382335 generic-all -gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#default 8382335 generic-all -gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#generational 8382335 generic-all -gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#default 8382335 generic-all +gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#generational 8386964 generic-all +gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#default 8386964 generic-all +gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#generational 8386964 generic-all +gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#default 8386964 generic-all ############################################################################# @@ -107,7 +104,6 @@ runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x runtime/Thread/TestAlwaysPreTouchStacks.java 8383372 macosx-aarch64 -applications/ctw/modules/jdk_jfr.java 8286300 linux-s390x applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 @@ -164,6 +160,7 @@ vmTestbase/metaspace/gc/firstGC_default/TestDescription.java 8208250 generic-all vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 8073470 linux-all vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t006/TestDescription.java 8372206 generic-all vmTestbase/nsk/jvmti/InterruptThread/intrpthrd003/TestDescription.java 8288911 macosx-all +vmTestbase/nsk/jvmti/unit/timers/JvmtiTest/TestDescription.java 8235348 windows-x64 vmTestbase/jit/escape/LockCoarsening/LockCoarsening001.java 8148743 generic-all vmTestbase/jit/escape/LockCoarsening/LockCoarsening002.java 8208259 generic-all diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 964c33bc57c..77f48171522 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -102,7 +102,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../../ notation to reach them diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index e09235f6a39..f400aa22f0b 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -314,7 +314,8 @@ tier1_gc_shenandoah = \ gc/shenandoah/compiler/ \ gc/shenandoah/mxbeans/ \ gc/shenandoah/TestSmallHeap.java \ - gc/shenandoah/oom/ + gc/shenandoah/oom/ \ + gtest/ShenandoahGtests.java tier2_gc_shenandoah = \ runtime/MemberName/MemberNameLeak.java \ diff --git a/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java b/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java index ba3d8aef191..b41b99b391b 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java +++ b/test/hotspot/jtreg/compiler/arguments/TestC1Globals.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,7 +27,8 @@ * @requires vm.debug * @summary Test flag with c1 value numbering * - * @run main/othervm -XX:+PrintValueNumbering -XX:+Verbose -XX:-UseLocalValueNumbering + * @run main/othervm -XX:-DisplayVMOutput + * -XX:+PrintValueNumbering -XX:+Verbose -XX:-UseLocalValueNumbering * -Xcomp -XX:TieredStopAtLevel=1 * compiler.arguments.TestC1Globals */ diff --git a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java index 534ec9d2d97..99cf06110d6 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java +++ b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java @@ -24,7 +24,7 @@ /* * @test * @key stress randomness - * @bug 8252219 8256535 8317349 8319879 8335334 8325478 + * @bug 8252219 8256535 8317349 8319879 8335334 8325478 8387940 * @requires vm.compiler2.enabled * @summary Tests that different combinations of stress options and * -XX:StressSeed=N are accepted. @@ -60,6 +60,10 @@ * compiler.arguments.TestStressOptions * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressMacroElimination -XX:StressSeed=42 * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressEliminateAllocations + * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressEliminateAllocations -XX:StressSeed=42 + * compiler.arguments.TestStressOptions */ package compiler.arguments; diff --git a/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java b/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java index df1c253b689..f61018738ae 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java +++ b/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java @@ -25,7 +25,8 @@ * @test * @summary Test running TraceTypeProfile enabled. * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+TraceTypeProfile compiler.arguments.TestTraceTypeProfile + * -XX:-DisplayVMOutput -XX:+TraceTypeProfile + * compiler.arguments.TestTraceTypeProfile */ package compiler.arguments; diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java b/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java new file mode 100644 index 00000000000..807f45f11f7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * 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 JDK-8387015 + * @summary C2: crash with "named projection 2 not found" from ArrayCopyNode::finish_transform() for clone + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined -XX:+StressIGVN + * -XX:StressSeed=1324432947 ${test.main.class} + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined -XX:+StressIGVN + * ${test.main.class} + */ + +package compiler.arraycopy; + +public class TestDeadCloneMem { + private static int field; + + public static void main(String[] args) { + int[] array = new int[10]; + array.clone(); + Object o = new Object(); + test1(42, false); + } + + private static int test1(int flag, boolean flag2) { + int len; + if (flag != 42) { + if (flag2) { + field = 42; + } + int[] array2; + if (flag != 42) { + len = -1; + array2 = new int[4]; + } else { + len = 42; + array2 = new int[100]; + } + int[] array = new int[len]; + int length = array.length; + int i = 0; + do { + synchronized (new Object()) {} + notInlined(); + array2.clone(); + i++; + } while (i < 10); + return length; + } + return 0; + } + + private static void notInlined() { + + } +} diff --git a/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java b/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java index e67a3679758..81069210ee1 100644 --- a/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java +++ b/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java @@ -25,7 +25,9 @@ * @test * @bug 8374518 * @summary Sanity check the flag -XX:+CITime and -XX:+CountLinearScan - * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+CITime -XX:+CountLinearScan ${test.main.class} + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+CITime -XX:+CountLinearScan + * ${test.main.class} */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java b/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java index d406438d39f..ba2e1b081f5 100644 --- a/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java +++ b/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, 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,7 +26,9 @@ * @summary load/store elimination will print out instructions without bcis. * @bug 8235383 * @requires vm.debug == true & vm.compiler1.enabled - * @run main/othervm -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp -XX:+PrintIRDuringConstruction -XX:+Verbose compiler.c1.TestPrintIRDuringConstruction + * @run main/othervm -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp + * -XX:-DisplayVMOutput -XX:+PrintIRDuringConstruction -XX:+Verbose + * compiler.c1.TestPrintIRDuringConstruction */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java b/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java index eff52cce7bb..b712fd2bdf3 100644 --- a/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java +++ b/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -29,8 +29,8 @@ * The test should bail out in C1. * * @compile TestTooManyVirtualRegisters.jasm - * @run main/othervm -Xbatch -XX:CompileCommand=dontinline,compiler.c1.TestExceptionBlockWithPredecessors::* - * compiler.c1.TestTooManyVirtualRegistersMain + * @run main/othervm -Xbatch -XX:CompileCommand=dontinline,compiler.c1.TestTooManyVirtualRegisters::* + * ${test.main.class} */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java b/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java index 233498d3c04..7eafb5b1c50 100644 --- a/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java +++ b/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.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 @@ -27,7 +27,8 @@ * @summary Sanity check the flag TraceLinearScanLevel with the highest level in a silent HelloWorld program. * * @requires vm.debug == true & vm.compiler1.enabled & vm.compMode != "Xcomp" - * @run main/othervm -Xbatch -XX:TraceLinearScanLevel=4 compiler.c1.TestTraceLinearScanLevel + * @run main/othervm -Xbatch -XX:-DisplayVMOutput -XX:TraceLinearScanLevel=4 + * compiler.c1.TestTraceLinearScanLevel */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java b/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java index d0bce024696..14c4f7b5a48 100644 --- a/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java +++ b/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java @@ -38,7 +38,7 @@ import compiler.lib.ir_framework.*; * @summary Tests to ensure that reachabilityFence() correctly keeps objects from being collected prematurely. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/othervm -Xbatch compiler.c2.ReachabilityFenceTest + * @run driver ${test.main.class} */ public class ReachabilityFenceTest { private static final int SIZE = 100; diff --git a/test/hotspot/jtreg/compiler/c2/TestDeadLoopLateInlining.java b/test/hotspot/jtreg/compiler/c2/TestDeadLoopLateInlining.java new file mode 100644 index 00000000000..893cfc9cd43 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestDeadLoopLateInlining.java @@ -0,0 +1,130 @@ +/* + * 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 8375694 + * @summary C2: Dead loop constructed with CastPP in late inlining + * @run main ${test.main.class} + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline + * -XX:CompileOnly=${test.main.class}::test* -Xcomp ${test.main.class} + */ + +package compiler.c2; + +public class TestDeadLoopLateInlining { + private static Object fieldObject; + private static int field; + private static A fieldA = new A(); + private static B fieldB = new B(); + + public static void main(String[] args) { + test1(0, true); + test2(0, 0, true); + } + + private static Object test1(int j, boolean flag) { + if (j < 42) { + if (flag) { + field = 42; + } + Object o = fieldObject; + if (j >= 42) { + for (int i = 1; i < 10; ) { + boolean boolRes = lateInlined2(); + if (boolRes) { + i *= 2; + o = lateInlined1(o); + if (o == null) { + throw new RuntimeException(); + } + } else { + i++; + } + } + } + return o; + } + return null; + } + + private static Object test2(int j, int k, boolean flag) { + A a; + if (k < 42) { + if (flag) { + field = 42; + } + if (k >= 42) { + a = fieldA; + } else { + a = fieldB; + } + if (a == null) { + throw new RuntimeException("never taken"); + } + if (j < 42) { + if (flag) { + field = 42; + } + Object o = fieldObject; + if (j >= 42) { + for (int i = 1; i < 10; ) { + boolean boolRes = lateInlined2(); + if (boolRes) { + i *= 2; + o = a.lateInlined(o); + if (o == null) { + throw new RuntimeException(); + } + } else { + i++; + } + } + } + return o; + } + } + return null; + } + + private static boolean lateInlined2() { + return true; + } + + private static Object lateInlined1(Object o) { + return o; + } + + + static class A { + Object lateInlined(Object o) { + return o; + } + } + + static class B extends A { + Object lateInlined(Object o) { + return o; + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestFindNode.java b/test/hotspot/jtreg/compiler/c2/TestFindNode.java index fa545da7e58..09f94a18f69 100644 --- a/test/hotspot/jtreg/compiler/c2/TestFindNode.java +++ b/test/hotspot/jtreg/compiler/c2/TestFindNode.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 @@ -27,7 +27,8 @@ * @requires vm.debug == true & vm.flavor == "server" * @summary Test which uses some special flags in order to test Node::find() in debug builds which could result in an endless loop or a stack overflow crash. * - * @run main/othervm -Xbatch -XX:CompileCommand=option,*::*,bool,Vectorize,true + * @run main/othervm -Xbatch -XX:-DisplayVMOutput + * -XX:CompileCommand=option,*::*,bool,Vectorize,true * -XX:+PrintOpto -XX:+TraceLoopOpts compiler.c2.TestFindNode */ package compiler.c2; diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java index 5e6a757dd5f..99143f04dcd 100644 --- a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java +++ b/test/hotspot/jtreg/compiler/c2/TestMergeStores.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 @@ -38,7 +38,7 @@ import java.util.Random; * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores aligned + * @run driver/timeout=480 ${test.main.class} aligned */ /* @@ -48,7 +48,7 @@ import java.util.Random; * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores unaligned + * @run driver/timeout=480 ${test.main.class} unaligned */ /* @@ -58,7 +58,7 @@ import java.util.Random; * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores StressIGVN + * @run driver/timeout=480 ${test.main.class} StressIGVN */ public class TestMergeStores { diff --git a/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java b/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java new file mode 100644 index 00000000000..31dd52bd3f6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java @@ -0,0 +1,117 @@ +/* + * 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 8379327 + * @summary Verify correctness for combined low/high 64-bit multiplication patterns. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.c2; + +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import java.math.BigInteger; + +public class TestMultiplyHighLowFusion { + private static final BigInteger MASK_64 = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); + private static final Generator LONG_GEN = Generators.G.longs(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bMulHiLoL\\b", "1"}) + public static long doMath(long a, long b) { + long low = a * b; + long high = Math.multiplyHigh(a, b); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bMulHiLoL\\b", "1"}) + public static long doMathSwapped(long a, long b) { + long low = b * a; + long high = Math.multiplyHigh(b, a); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bUMulHiLoL\\b", "1"}) + public static long doUnsignedMath(long a, long b) { + long low = a * b; + long high = Math.unsignedMultiplyHigh(a, b); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bUMulHiLoL\\b", "1"}) + public static long doUnsignedMathSwapped(long a, long b) { + long low = b * a; + long high = Math.unsignedMultiplyHigh(b, a); + return low + high; + } + + @Run(test = {"doMath", "doMathSwapped", "doUnsignedMath", "doUnsignedMathSwapped"}) + public void runTests() { + verifyPair(LONG_GEN.next(), LONG_GEN.next()); + } + + private void verifyPair(long a, long b) { + long expectedSigned = expectedSigned(a, b); + long expectedUnsigned = expectedUnsigned(a, b); + + if (doMath(a, b) != expectedSigned) { + throw new RuntimeException("Signed mismatch for a=" + a + ", b=" + b); + } + if (doMathSwapped(a, b) != expectedSigned) { + throw new RuntimeException("Signed swapped mismatch for a=" + a + ", b=" + b); + } + if (doUnsignedMath(a, b) != expectedUnsigned) { + throw new RuntimeException("Unsigned mismatch for a=" + a + ", b=" + b); + } + if (doUnsignedMathSwapped(a, b) != expectedUnsigned) { + throw new RuntimeException("Unsigned swapped mismatch for a=" + a + ", b=" + b); + } + } + + private static long expectedSigned(long a, long b) { + BigInteger product = BigInteger.valueOf(a).multiply(BigInteger.valueOf(b)); + long low = product.longValue(); + long high = product.shiftRight(64).longValue(); + return low + high; + } + + private static long expectedUnsigned(long a, long b) { + BigInteger ua = BigInteger.valueOf(a).and(MASK_64); + BigInteger ub = BigInteger.valueOf(b).and(MASK_64); + BigInteger product = ua.multiply(ub); + long low = product.longValue(); + long high = product.shiftRight(64).longValue(); + return low + high; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java b/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java index af252265b76..aeb391ea15f 100644 --- a/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java +++ b/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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,7 +27,8 @@ * @requires vm.debug == true & vm.compiler2.enabled * @summary Run with -Xcomp -XX:-TieredCompilation to force C2 compilations to test -XX:+PrintIdealNodeCount in debug builds. * - * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+PrintIdealNodeCount compiler.c2.TestPrintIdealNodeCount + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:-DisplayVMOutput + * -XX:+PrintIdealNodeCount compiler.c2.TestPrintIdealNodeCount */ package compiler.c2; diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java index 1146d189ce2..ccb00c635c5 100644 --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java +++ b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.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 @@ -32,7 +32,6 @@ * -XX:CompileCommand=compileonly,*::allocateInstance * -XX:CompileCommand=dontinline,*TestReduceAllocationAndNonExactAllocate*::* * -XX:+UnlockDiagnosticVMOptions - * -XX:+TraceReduceAllocationMerges * -XX:-TieredCompilation * -Xbatch * -Xcomp diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java index 114f7f9bfab..084f63279bf 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java @@ -89,15 +89,19 @@ public class TestTrampoline { } static class Test { - private static void test(String s, int i) { + // Use a StringBuilder to avoid issues with String.charAt() not being + // inlined on Windows because its UTF-16 path was executed at startup + // but not enough for C2 to inline it. + private static void test(StringBuilder s, int i) { if (s.charAt(i) > 128) throw new RuntimeException(); } public static void main(String[] args) { - String s = "Returns the char value at the specified index."; + var sb = new StringBuilder(); + sb.append("Returns the char value at the specified index."); for (int i = 0; i < ITERATIONS_TO_HEAT_LOOP; ++i) { - test(s, i % s.length()); + test(sb, i % sb.length()); } } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java index ba7e7d851b0..66dabcebf80 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java @@ -31,7 +31,7 @@ import compiler.lib.ir_framework.*; * @summary Test barriers emitted in constructors * @library /test/lib / * @requires os.arch=="aarch64" | os.arch=="riscv64" | os.arch=="x86_64" | os.arch=="amd64" - * @run main compiler.c2.irTests.ConstructorBarriers + * @run driver ${test.main.class} */ public class ConstructorBarriers { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java index 5524b5d7b6c..9556fce988d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. All rights reserved. - * 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 @@ -26,7 +26,6 @@ package compiler.c2.irTests; import compiler.lib.ir_framework.*; import jdk.test.lib.Utils; -import jdk.test.whitebox.WhiteBox; import jdk.internal.misc.Unsafe; import java.util.Random; import java.util.Arrays; @@ -40,15 +39,12 @@ import java.util.List; * @summary C2: vectorization fails on simple ByteBuffer loop * @modules java.base/jdk.internal.misc * @library /test/lib / - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.TestVectorizationMismatchedAccess + * @run driver ${test.main.class} */ public class TestVectorizationMismatchedAccess { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); private static final Random RANDOM = Utils.getRandomInstance(); - private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { TestFramework framework = new TestFramework(); diff --git a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java index 2c84ad2676e..8f24cb46e20 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java @@ -43,7 +43,6 @@ public class AllocationMergesTests { Scenario scenario0 = new Scenario(0, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:+UseCompressedOops", "-XX:CompileCommand=inline,*::charAt*", @@ -54,7 +53,6 @@ public class AllocationMergesTests { Scenario scenario1 = new Scenario(2, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:-UseCompressedOops", "-XX:CompileCommand=inline,*::charAt*", @@ -65,7 +63,6 @@ public class AllocationMergesTests { Scenario scenario2 = new Scenario(3, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:+UseCompressedOops", "-XX:-OptimizePtrCompare", diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java index 8b3abbb0300..82bb79d3c1f 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.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. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,7 @@ * * @library /test/lib / * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*zbkb.*" - * @run main/othervm compiler.c2.riscv64.TestIntegerReverse + * @run driver ${test.main.class} */ package compiler.c2.riscv64; diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java index 01c3b871ffa..807a58a18f3 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.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. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,7 @@ * * @library /test/lib / * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*zbkb.*" - * @run main/othervm compiler.c2.riscv64.TestLongReverse + * @run driver ${test.main.class} */ package compiler.c2.riscv64; diff --git a/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java b/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java new file mode 100644 index 00000000000..732ba6f3502 --- /dev/null +++ b/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java @@ -0,0 +1,60 @@ +/* + * 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 compiler.ccp; + +/* + * @test + * @bug 8379555 + * @summary Test that CompressBitsNode::Value does not violate monotonicity + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test* ${test.main.class} + */ +public class TestCompressBitsMonotonicity { + public static void main(String[] args) { + for (int i = 0; i < 10000; i++) { + testInt(0); + testLong(0); + } + } + + private static int testInt(int v) { + v &= 0b1101; + int mask = 0b1111; + int sum = 0; + for (int i = 1; i < 10; i *= 2) { + mask = Integer.compress(v, mask); + sum += mask; + } + return sum; + } + + private static long testLong(long v) { + v &= 0b1101; + long mask = 0b1111; + long sum = 0; + for (int i = 1; i < 10; i *= 2) { + mask = Long.compress(v, mask); + sum += mask; + } + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java b/test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java new file mode 100644 index 00000000000..27562575459 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java @@ -0,0 +1,138 @@ +/* + * 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 8378719 + * @summary Reproduces a RuntimeStub::resolve_static_call_blob pd_patch_instruction_size guarantee + * - forces adapters to be allocated outside the NonNMethod heap + * - puts c2i adapter and compiled method at 128+ MB distance + * @requires vm.flagless + * @requires os.arch == "aarch64" + * @requires vm.debug == false + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+UnlockExperimentalVMOptions + * -XX:+WhiteBoxAPI + * -XX:ReservedCodeCacheSize=240M + * -XX:NonNMethodCodeHeapSize=8M + * -XX:ProfiledCodeHeapSize=116M + * -XX:NonProfiledCodeHeapSize=116M + * -XX:CodeCacheMinBlockLength=1 + * -XX:CodeCacheSegmentSize=128 + * -XX:-UseCodeCacheFlushing + * -XX:CompileCommand=dontinline,compiler.codecache.TestNonNMethodHeapOverflowTarget::a + * -XX:CompileCommand=exclude,compiler.codecache.TestNonNMethodHeapOverflowTarget::a + * -XX:CompileCommand=compileonly,compiler.codecache.TestNonNMethodHeapOverflowTarget::b + * compiler.codecache.TestNonNMethodHeapOverflow + */ + +package compiler.codecache; + +import jdk.test.whitebox.WhiteBox; +import jdk.test.whitebox.code.BlobType; +import jdk.test.whitebox.code.CodeBlob; +import jdk.test.whitebox.code.NMethod; + +import java.lang.reflect.Method; + +public class TestNonNMethodHeapOverflow { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final int HEAP_BLOCK_HEADER_SIZE = 8; + + public static void main(String[] args) throws Exception { + WB.lockCompilation(); + + BlobType blobType; + int blobSize = 1024; + int allocSize = blobSize - HEAP_BLOCK_HEADER_SIZE; + // fill the NonNMethod heap + do { + long addr = WB.allocateCodeBlob(allocSize, BlobType.NonNMethod.id); + if (addr == 0) { + throw new RuntimeException("Failed to allocate in BlobType.NonNMethod"); + } + blobType = CodeBlob.getCodeBlob(addr).code_blob_type; + } while (blobType == BlobType.NonNMethod); + + if (blobType != BlobType.MethodNonProfiled) { + throw new RuntimeException("NonNMethod->NonProfiled fallback mechanism was changed? Need to update the test"); + } + + long heapSize = BlobType.MethodNonProfiled.getSize(); + int allocated = 0; + // fill the first half of NonProfiled heap + while (allocated < heapSize / 2) { + long addr = WB.allocateCodeBlob(allocSize, BlobType.MethodNonProfiled.id); + if (addr == 0) { + throw new RuntimeException("Failed to allocate in MethodNonProfiled"); + } + allocated += blobSize; + } + + WB.unlockCompilation(); + + // loading triggers i2c/c2i adapter generation; NonNMethod heap is full, adapters go into a middle of NonProfiled heap + Class c = Class.forName("compiler.codecache.TestNonNMethodHeapOverflowTarget"); + Method methodB = c.getDeclaredMethod("b"); + methodB.invoke(null); + + // compile b() at level 2 so the nmethod goes into the beginning of Profiled heap + int compLevel = 2; + WB.enqueueMethodForCompilation(methodB, compLevel); + while (WB.isMethodQueuedForCompilation(methodB)) { + Thread.sleep(100); + } + if (WB.getMethodCompilationLevel(methodB) != compLevel) { + throw new IllegalStateException("b() is not compiled at the compilation level " + compLevel + + ". Got: " + WB.getMethodCompilationLevel(methodB)); + } + + // The distance from the static call stub in nmethod to the c2i adapter exceeds 128MB (AArch64 near-branch range): + // + // | Profiled | NonNMethod | NonProfiled | + // -------------------------------- ------------ -------------------------------- + // |[nmethod] |############|################[c2i] | + + NMethod nm = NMethod.get(methodB, false); + System.out.println("b() at 0x" + Long.toHexString(nm.address) + " heap=" + nm.code_blob_type); + if (nm.code_blob_type != BlobType.MethodProfiled) { + throw new RuntimeException("b() is expected to be in MethodProfiled heap, got: " + nm.code_blob_type); + } + + // invoke compiled b(): triggers resolve_static_call_blob to patch the static call stub + // in nmethod to point to the c2i adapter for a() + methodB.invoke(null); + } +} + +class TestNonNMethodHeapOverflowTarget { + static float a(float f1, double d1, long l1, int i1, float f2, double d2) { + return f1; + } + static float b() { + return a(1.0f, 2.0, 3L, 4, 5.0f, 6.0); + } +} diff --git a/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java new file mode 100644 index 00000000000..3d752cc74f5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.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. + */ +package compiler.controldependency; + +/* + * @test + * @bug 8385420 + * @summary C2 correctly handles the case when the removed CastPPNode has a CMove use. + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class} + * + */ +public class TestRemoveCastPPWithCMoveUse { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(null, false); + test(null, true); + test("", false); + test("", true); + } + } + + static int test(String a, boolean flag) { + StringBuilder sb = new StringBuilder(); + if (a == null) { + sb.append(""); + } else { + sb.append(flag ? a : ""); + } + return sb.length(); + } +} diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java b/test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java new file mode 100644 index 00000000000..c31514bbba1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java @@ -0,0 +1,57 @@ +/* + * 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 8388186 + * @summary Test for VM crash with -XX:+EnableX86ECoreOpts and UseAVX < 2. + * @requires vm.flagless + * @requires os.arch == "amd64" | os.arch == "x86_64" + * @library /test/lib + * @run driver ${test.main.class} + */ + +package compiler.cpuflags; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestEnableX86ECoreOptsWithAVX2Disabled { + static final String[] OPTIONS = { + "-XX:UseSSE=2", + "-XX:UseSSE=3", + "-XX:UseAVX=0", + "-XX:UseAVX=1" + }; + + public static void main(String[] args) throws Exception { + for (String option : OPTIONS) { + OutputAnalyzer output = ProcessTools.executeLimitedTestJava( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+EnableX86ECoreOpts", + option, + "-version"); + output.shouldHaveExitValue(0); + } + } +} diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java b/test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java new file mode 100644 index 00000000000..df595a5ff26 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java @@ -0,0 +1,62 @@ +/* + * 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 8386475 + * @summary Verify no assertions with -XX:+UseBMI2Instructions + * @requires os.simpleArch == "x64" + * @run main/othervm -XX:+UseBMI2Instructions ${test.main.class} + */ + +/* + * @test + * @bug 8386475 + * @summary Verify no assertions with -XX:-UseBMI2Instructions + * @requires os.simpleArch == "x64" + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,java.lang.CharacterDataLatin1:: -XX:+UnlockDiagnosticVMOptions -XX:CopyAVX3Threshold=0 -XX:-UseBMI2Instructions ${test.main.class} + */ + +/* + * @test + * @bug 8386475 + * @summary Verify no assertions when generating vectorizedMismatch stub with -XX:-UseBMI2Instructions + * @requires os.simpleArch == "x64" & vm.cpu.features ~= ".*avx2.*" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 -XX:-UseBMI2Instructions ${test.main.class} + */ + +/* + * @test + * @bug 8386475 + * @summary Verify no assertions when generating string_indexof stub with -XX:-UseBMI2Instructions + * @requires os.simpleArch == "x64" & vm.cpu.features ~= ".*avx2.*" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseAVX=2 -XX:+EnableX86ECoreOpts -XX:-UseBMI2Instructions ${test.main.class} + */ + +package compiler.cpuflags; + +public class TestUseBMI2Instructions { + public static void main(String args[]) { + // intentionally empty + } +} diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java b/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java new file mode 100644 index 00000000000..0c7d04486f2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.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. + */ + +/** + * @test + * @bug 8386656 + * @summary Verify no assertions when running with -XX:-UseCountTrailingZerosInstruction + * @requires os.simpleArch == "x64" + * @run main/othervm -Xbatch -XX:-UseCountTrailingZerosInstruction ${test.main.class} + */ + +/** + * @test + * @bug 8386656 + * @summary Verify no assertions when running with -XX:+UseCountTrailingZerosInstruction + * @requires os.simpleArch == "x64" + * @run main/othervm -Xbatch -XX:+UseCountTrailingZerosInstruction ${test.main.class} + */ + +package compiler.cpuflags; + +import java.util.Arrays; + +public class TestUseCountTrailingZerosInstruction { + public static void main(String[] args) { + byte[] a = new byte[32]; + byte[] b = new byte[32]; + for (int i = 0; i < 20_000; i++) { + Arrays.mismatch(a, b); + } + } +} + diff --git a/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java b/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java index 1a3fdf6e9d6..551c22c1377 100644 --- a/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java +++ b/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java @@ -26,7 +26,7 @@ * @bug 8382057 * @requires vm.debug == true * - * @run main/othervm -Xbatch -XX:+CountCompiledCalls ${test.main.class} + * @run main/othervm -Xbatch -XX:-DisplayVMOutput -XX:+CountCompiledCalls ${test.main.class} */ package compiler.debug; diff --git a/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java b/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java index 042abc23fcc..8ad971bc68d 100644 --- a/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java +++ b/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.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 @@ -28,7 +28,9 @@ package compiler.debug; * @bug 8344013 * @requires vm.debug == true & vm.compiler2.enabled * @summary Verify the xmlStream log stack is not left in a bad state - * @run main/othervm -XX:+LogCompilation -XX:CompileCommand=log,*.* -XX:+CITimeVerbose -Xcomp compiler.debug.TestLogStackAssert + * @run main/othervm -XX:-DisplayVMOutput -XX:+LogCompilation + * -XX:CompileCommand=log,*.* -XX:+CITimeVerbose -Xcomp + * compiler.debug.TestLogStackAssert */ public class TestLogStackAssert { diff --git a/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java b/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java index b46aac9a824..6de08c97329 100644 --- a/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java +++ b/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 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 @@ -27,9 +27,10 @@ * @requires vm.debug == true & vm.compiler2.enabled * @modules java.base/jdk.internal.misc * - * @run main/othervm -Xbatch -XX:CompileCommand=dontinline,compiler.debug.TestTracePhaseCCP::test - * -XX:CompileCommand=compileonly,compiler.debug.TestTracePhaseCCP::test -XX:+TracePhaseCCP - * compiler.debug.TestTracePhaseCCP + * @run main/othervm -Xbatch -XX:-DisplayVMOutput + * -XX:CompileCommand=dontinline,compiler.debug.TestTracePhaseCCP::test + * -XX:CompileCommand=compileonly,compiler.debug.TestTracePhaseCCP::test + * -XX:+TracePhaseCCP compiler.debug.TestTracePhaseCCP */ package compiler.debug; diff --git a/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java b/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java index 8e6169f07dc..9d31cabd825 100644 --- a/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java +++ b/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,7 +25,7 @@ /* * @test * @requires vm.debug == true & vm.compiler2.enabled - * @run main/othervm -Xbatch -XX:-TieredCompilation + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:-DisplayVMOutput * -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceIterativeGVN * compiler.debug.TraceIterativeGVN */ diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java b/test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java new file mode 100644 index 00000000000..b97847b9b28 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java @@ -0,0 +1,67 @@ +/* + * 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 8387940 + * @requires vm.compiler2.enabled + * @summary C2: Stress allocation elimination failures + * + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.escapeAnalysis; + +import compiler.lib.ir_framework.*; + +public class StressEliminateAllocationsIRTest { + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:+StressEliminateAllocations", + "-XX:StressEliminateAllocationsMean=1"); + } + + static class A { + final int i; + A(int i) { + this.i = i; + } + } + + @Test + @IR(counts = {IRNode.ALLOC, "1"}) + @Arguments(values = Argument.NUMBER_42) + private static int test(int i) { + // Even though the object is scalar replaceable, + // allocation elimination unconditionally fails in stress mode. + A a = new A(i); + + dontInline(); + + return a.i; + } + + @DontInline + private static void dontInline() {} +} diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java new file mode 100644 index 00000000000..f33a16785d1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java @@ -0,0 +1,160 @@ +/* + * 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 8216486 + * @summary Verify BCEscapeAnalyzer handles methods where + * (numblocks+1)*(max_stack+max_locals) overflows a 32-bit int. + * On a UBSAN build the signed overflow would be caught as UB; + * on a normal build the test verifies no crash from the bogus + * allocation size that resulted from the overflow. + * + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xcomp -XX:-TieredCompilation + * compiler.escapeAnalysis.TestBCEscapeAnalyzerOverflow + */ + +package compiler.escapeAnalysis; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Label; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class TestBCEscapeAnalyzerOverflow { + + // Number of goto instructions in the generated method. + // Creates NUM_GOTOS + 1 basic blocks. With max_stack = 0xFFFF and + // max_locals = 0xFFFF the product (numblocks+1)*(max_stack+max_locals) + // is 16386 * 131070 = 2,147,713,020 which exceeds Integer.MAX_VALUE. + static final int NUM_GOTOS = 16384; + static final int TARGET_MAX_STACK = 0xFFFF; + static final int TARGET_MAX_LOCALS = 0xFFFF; + + static final ClassDesc CD_HELPER = + ClassDesc.of("compiler.escapeAnalysis.BCEscapeOverflowHelper"); + + public static void main(String[] args) throws Throwable { + byte[] classBytes = buildClass(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class cls = lookup.defineClass(classBytes); + + // caller() allocates an Object and passes it to bigMethod() via + // invokestatic. Under -Xcomp -XX:-TieredCompilation, C2 compiles + // caller() and invokes BCEscapeAnalyzer on bigMethod to determine + // whether the argument escapes. Without the fix the 32-bit + // overflow in iterate_blocks leads to undefined behavior. + var mh = lookup.findStatic(cls, "caller", + MethodType.methodType(void.class)); + mh.invoke(); + } + + /** + * Builds a minimal class (version 50, no StackMapTable needed) with: + * public static void bigMethod(Object o) -- pathological method + * public static void caller() -- calls bigMethod + * + * The ClassFile API generates the bytecode; max_stack and max_locals + * of bigMethod are then patched to the target overflow-triggering values. + */ + static byte[] buildClass() { + var mtd_Obj_void = MethodTypeDesc.of(ConstantDescs.CD_void, + ConstantDescs.CD_Object); + var mtd_void = MethodTypeDesc.of(ConstantDescs.CD_void); + + byte[] bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS) + .build(CD_HELPER, cb -> { + cb.withVersion(50, 0); + cb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER); + + // bigMethod(Object o): aload_0, pop, , return + cb.withMethod("bigMethod", mtd_Obj_void, + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(code -> { + code.aload(0); + code.pop(); + for (int i = 0; i < NUM_GOTOS; i++) { + Label next = code.newLabel(); + code.goto_(next); + code.labelBinding(next); + } + code.return_(); + })); + + // caller(): new Object → dup → invokespecial → + // invokestatic bigMethod → return + cb.withMethod("caller", mtd_void, + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(code -> { + code.new_(ConstantDescs.CD_Object); + code.dup(); + code.invokespecial(ConstantDescs.CD_Object, + "", mtd_void); + code.invokestatic(CD_HELPER, + "bigMethod", mtd_Obj_void); + code.return_(); + })); + }); + + patchBigMethodMaxes(bytes); + return bytes; + } + + /** + * Locates bigMethod's Code attribute and patches max_stack/max_locals + * to TARGET_MAX_STACK/TARGET_MAX_LOCALS. The ClassFile API computes + * small values (max_stack=1, max_locals=1); we inflate them to create + * the pathological overflow case. + * + * The Code attribute layout is: + * attribute_name_index(u2), attribute_length(u4), + * max_stack(u2), max_locals(u2), code_length(u4), code[...]... + * + * We search for bigMethod's unique code_length and patch the two u2 + * fields immediately before it. + */ + static void patchBigMethodMaxes(byte[] b) { + int expectedCodeLen = NUM_GOTOS * 3 + 3; + for (int i = 4; i <= b.length - 4; i++) { + int codeLen = ((b[i] & 0xFF) << 24) | ((b[i + 1] & 0xFF) << 16) + | ((b[i + 2] & 0xFF) << 8) | (b[i + 3] & 0xFF); + if (codeLen == expectedCodeLen) { + int ms = ((b[i - 4] & 0xFF) << 8) | (b[i - 3] & 0xFF); + int ml = ((b[i - 2] & 0xFF) << 8) | (b[i - 1] & 0xFF); + if (ms <= 2 && ml <= 2) { + b[i - 4] = (byte)(TARGET_MAX_STACK >>> 8); + b[i - 3] = (byte)(TARGET_MAX_STACK); + b[i - 2] = (byte)(TARGET_MAX_LOCALS >>> 8); + b[i - 1] = (byte)(TARGET_MAX_LOCALS); + return; + } + } + } + throw new RuntimeException("Could not find bigMethod Code attribute"); + } +} diff --git a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java index 9be192d1f55..026b2d15b77 100644 --- a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java +++ b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java @@ -43,7 +43,7 @@ import test.java.lang.invoke.lib.InstructionHelper; * @library /test/lib /test/jdk/java/lang/invoke/common / * @build test.java.lang.invoke.lib.InstructionHelper * - * @run main/othervm ${test.main.class} + * @run driver ${test.main.class} */ public class TestDebugDuringExceptionCatching { diff --git a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorJFR.java b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorJFR.java new file mode 100644 index 00000000000..97c0a9c06ec --- /dev/null +++ b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorJFR.java @@ -0,0 +1,46 @@ +/* + * 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. + * + */ + +/* + * @test + * @bug 8385651 + * @summary Verify the HotCodeSampler and JFR do not attempt to suspend the same JavaThread and crash + * @requires vm.compiler2.enabled & vm.hasJFR + * @run main/othervm -XX:StartFlightRecording -XX:+UnlockExperimentalVMOptions -XX:+HotCodeHeap -XX:+NMethodRelocation -XX:+UnlockDiagnosticVMOptions + * -XX:HotCodeIntervalSeconds=0 -XX:HotCodeStartupDelaySeconds=0 -XX:HotCodeStablePercent=-1 -Xlog:hotcode=debug + * compiler.hotcode.HotCodeCollectorJFR + */ + +package compiler.hotcode; + +public class HotCodeCollectorJFR { + + private static final int FUNC_RUN_MILLIS = 10_000; + + public static void main(String[] args) throws Exception { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) {} + } + +} diff --git a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java index 5677ca88eb2..2b93c24e255 100644 --- a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java +++ b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java @@ -79,6 +79,8 @@ public class HotCodeCollectorMoveFunction { private static final int C2_LEVEL = 4; private static final int FUNC_RUN_MILLIS = 60_000; + private static volatile int blackholeCount = 0; + static { try { method = Runner.class.getMethod("func"); @@ -111,7 +113,15 @@ public class HotCodeCollectorMoveFunction { public static void func() { long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) {} + while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) { + // Perform multiplicative LCG to ensure the compiler does not optimize away the code. + // Integer overflow is used for the modulus so the loop terminates after (2^32)/4 iterations + int num = 1; + do { + blackholeCount++; + num *= 69069; + } while (num != 1); + } } } } diff --git a/test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java b/test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java new file mode 100644 index 00000000000..d950908eeaa --- /dev/null +++ b/test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java @@ -0,0 +1,334 @@ +/* + * 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 TestIdealGraphDump + * @bug 8370870 + * @summary Verify that IGV graph dumping produces well-structured XML at different print levels + * @library /test/lib + * @requires vm.debug == true & vm.compiler2.enabled & vm.flagless + * @run driver ${test.main.class} + */ + +package compiler.igv; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestIdealGraphDump { + + private static final String TEST_CLASS = TestMethods.class.getName(); + private static final String METHOD_COMPUTE = TEST_CLASS + "::compute"; + private static final String METHOD_BRANCH = TEST_CLASS + "::branchyMethod"; + + private static final Map dumpCache = new HashMap<>(); + + public static void main(String[] args) throws Exception { + testDisabled(); + testLevel0(); + testLevel1(); + testLevel2(); + testLevel3(); + testLevel4(); + testLevel5(); + testLevel6(); + testMonotonicallyIncreasingGraphCounts(); + testXmlWellFormedness(); + testMethodNameInGraph(); + testMultipleMethods(); + testIGVPrintLevelDirective(); + } + + private static void testDisabled() throws Exception { + Path xmlFile = getCachedDump(-1); + Asserts.assertTrue(Files.size(xmlFile) == 0, + "Level -1 (disabled) must produce an empty file"); + } + + private static void testLevel0() throws Exception { + Path xmlFile = getCachedDump(0); + Asserts.assertTrue(Files.size(xmlFile) == 0, + "Level 0 must produce an empty file (no system-wide dumps)"); + } + + private static void testLevel1() throws Exception { + String content = getCachedContent(1); + assertContainsPhase(content, "After Parsing", 1); + assertContainsPhase(content, "Before Matching", 1); + assertContainsPhase(content, "Final Code", 1); + assertNotContainsPhase(content, "PhaseCCP 1", 1); + } + + private static void testLevel2() throws Exception { + String content = getCachedContent(2); + assertContainsPhase(content, "After Parsing", 2); + assertContainsPhase(content, "Final Code", 2); + assertContainsPhase(content, "Iter GVN 1", 2); + assertContainsPhase(content, "PhaseCCP 1", 2); + assertNotContainsPhase(content, "Before Macro Expansion", 2); + } + + private static void testLevel3() throws Exception { + String content = getCachedContent(3); + assertContainsPhase(content, "Before Macro Expansion", 3); + assertNotContainsPhase(content, "Initial Liveness", 3); + } + + private static void testLevel4() throws Exception { + String content = getCachedContent(4); + assertContainsPhase(content, "Initial Liveness", 4); + assertNotContainsPhase(content, "After Iter GVN Step", 4); + } + + private static void testLevel5() throws Exception { + String content = getCachedContent(5); + assertContainsPhase(content, "After Iter GVN Step", 5); + assertNotContainsPhase(content, "Bytecode", 5); + } + + private static void testLevel6() throws Exception { + String content = getCachedContent(6); + Asserts.assertTrue(containsPhase(content, "Bytecode"), + "Level 6 must contain per-bytecode graphs (e.g., 'Bytecode 0: ...')"); + } + + private static void testMonotonicallyIncreasingGraphCounts() throws Exception { + int prevCount = 0; + for (int level = 1; level <= 6; level++) { + String content = getCachedContent(level); + int count = countGraphs(content); + Asserts.assertTrue(count >= prevCount, + "Level " + level + " (" + count + " graphs) must have at least as many as level " + + (level - 1) + " (" + prevCount + " graphs)"); + prevCount = count; + } + } + + private static void testXmlWellFormedness() throws Exception { + Path xmlFile = getCachedDump(2); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + try { + builder.parse(xmlFile.toFile()); + } catch (Exception e) { + Asserts.fail("IGV XML at level 2 is not well-formed: " + e.getMessage()); + } + + String content = getCachedContent(2); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain closing "); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(" elements"); + Asserts.assertTrue(content.contains(""); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain "); + } + + private static void testMethodNameInGraph() throws Exception { + String content = getCachedContent(1); + Asserts.assertTrue(content.contains("TestMethods.compute"), + "Graph output must contain the compiled method name 'TestMethods.compute'"); + } + + private static void testMultipleMethods() throws Exception { + Path xmlFile = dumpMultipleMethods(1); + String content = Files.readString(xmlFile); + + Asserts.assertTrue(content.contains("TestMethods.compute"), + "Must contain graphs for 'compute' method"); + Asserts.assertTrue(content.contains("TestMethods.branchyMethod"), + "Must contain graphs for 'branchyMethod' method"); + + int computeFinalCode = countMethodPhase(content, "TestMethods.compute", "Final Code"); + int branchFinalCode = countMethodPhase(content, "TestMethods.branchyMethod", "Final Code"); + Asserts.assertEquals(computeFinalCode, 1, + "compute must emit exactly one 'Final Code' graph, got " + computeFinalCode); + Asserts.assertEquals(branchFinalCode, 1, + "branchyMethod must emit exactly one 'Final Code' graph, got " + branchFinalCode); + } + + private static void testIGVPrintLevelDirective() throws Exception { + Path xmlFile = Files.createTempFile("igv_directive_", ".xml"); + xmlFile.toFile().deleteOnExit(); + + List options = new ArrayList<>(); + options.add("-Xbatch"); + options.add("-XX:PrintIdealGraphLevel=0"); + options.add("-XX:PrintIdealGraphFile=" + xmlFile.toAbsolutePath()); + options.add("-XX:CompileCommand=IGVPrintLevel," + METHOD_COMPUTE + ",2"); + options.add(TEST_CLASS); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0); + oa.shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + + String content = Files.readString(xmlFile); + Asserts.assertTrue(Files.size(xmlFile) > 0, + "Per-method IGVPrintLevel directive must produce output even with system level 0"); + Asserts.assertTrue(content.contains("TestMethods.compute"), + "Directive-based dump must contain the target method"); + Asserts.assertFalse(content.contains("TestMethods.branchyMethod"), + "Directive-based dump must NOT contain non-targeted method"); + assertContainsPhase(content, "After Parsing", 2); + } + + private static Path getCachedDump(int level) throws Exception { + if (!dumpCache.containsKey(level)) { + dumpCache.put(level, dumpAtLevel(level)); + } + return dumpCache.get(level); + } + + private static String getCachedContent(int level) throws Exception { + return Files.readString(getCachedDump(level)); + } + + private static Path dumpAtLevel(int level) throws Exception { + Path xmlFile = Files.createTempFile("igv_level" + level + "_", ".xml"); + xmlFile.toFile().deleteOnExit(); + + List options = new ArrayList<>(); + options.add("-Xbatch"); + options.add("-XX:PrintIdealGraphLevel=" + level); + options.add("-XX:PrintIdealGraphFile=" + xmlFile.toAbsolutePath()); + options.add("-XX:CompileCommand=compileonly," + METHOD_COMPUTE); + options.add(TEST_CLASS); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0); + oa.shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + + return xmlFile; + } + + private static Path dumpMultipleMethods(int level) throws Exception { + Path xmlFile = Files.createTempFile("igv_multi_", ".xml"); + xmlFile.toFile().deleteOnExit(); + + List options = new ArrayList<>(); + options.add("-Xbatch"); + options.add("-XX:PrintIdealGraphLevel=" + level); + options.add("-XX:PrintIdealGraphFile=" + xmlFile.toAbsolutePath()); + options.add("-XX:CompileCommand=compileonly," + METHOD_COMPUTE); + options.add("-XX:CompileCommand=compileonly," + METHOD_BRANCH); + options.add(TEST_CLASS); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0); + oa.shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + + return xmlFile; + } + + private static int countGraphs(String content) { + return countOccurrences(content, "" + phaseName + "<") || + content.contains("'" + phaseName); + } + + private static void assertContainsPhase(String content, String phaseName, int level) { + Asserts.assertTrue(containsPhase(content, phaseName), + "Level " + level + " must contain phase '" + phaseName + "'"); + } + + private static void assertNotContainsPhase(String content, String phaseName, int level) { + Asserts.assertFalse(containsPhase(content, phaseName), + "Level " + level + " must NOT contain phase '" + phaseName + "'"); + } + + private static int countMethodPhase(String content, String methodName, String phaseName) { + int count = 0; + int groupStart = 0; + while ((groupStart = content.indexOf("", groupStart)) != -1) { + int groupEnd = content.indexOf("", groupStart); + if (groupEnd == -1) { + break; + } + String group = content.substring(groupStart, groupEnd); + if (group.contains(methodName)) { + count += countOccurrences(group, ""); + } + groupStart = groupEnd; + } + return count; + } + + private static int countOccurrences(String str, String sub) { + int count = 0; + int idx = 0; + while ((idx = str.indexOf(sub, idx)) != -1) { + count++; + idx += sub.length(); + } + return count; + } + + public static class TestMethods { + public static void main(String[] args) { + int sum = 0; + for (int i = 0; i < 20_000; i++) { + sum += compute(i, i + 1); + sum += branchyMethod(i, i % 7); + } + System.out.println(sum); + } + + static int compute(int a, int b) { + int result = 0; + for (int i = 0; i < a % 10; i++) { + result += b * i; + } + return result; + } + + static int branchyMethod(int x, int y) { + if (x > y) { + return x * y + 1; + } else if (x == y) { + return x + y; + } else { + return y - x; + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java b/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java index c5ef1640721..ab40a2ea234 100644 --- a/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java +++ b/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.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 @@ -27,7 +27,7 @@ * @summary Ranges can be proven to be disjoint but not orderable (thanks to unsigned range) * Comparing such values in such range with != should always be true. * @library /test/lib / - * @run main compiler.igvn.CmpDisjointButNonOrderedRangesLong + * @run driver ${test.main.class} */ package compiler.igvn; diff --git a/test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java b/test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java new file mode 100644 index 00000000000..fbae0454d07 --- /dev/null +++ b/test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java @@ -0,0 +1,225 @@ +/* + * 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 compiler.inlining; + +import java.lang.reflect.Field; +import jdk.internal.misc.Unsafe; +import jdk.test.lib.Asserts; + +/** + * @test + * @bug 8374783 + * @summary Test that address type refinements after an incremental inlining + * step are propagated by IGVN before the next step. Failing to + * propagate such refinements could lead to slice mismatches between + * field-derived and IGVN-recorded address types when parsing bytecode + * in subsequent inlining steps. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main ${test.main.class} + * @run main/othervm -Xbatch + -XX:CompileCommand=compileonly,${test.main.class}::test* + -XX:CompileCommand=dontinline,${test.main.class}::notInlined* + -XX:CompileCommand=delayinline,${test.main.class}::late* + ${test.main.class} + */ + +class A { + int f; +} + +public class TestLateInliningWithSliceNarrowing { + + private static Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final long F_OFFSET; + private static final long INT_ARRAY_OFFSET; + + static { + try { + Field fField = A.class.getDeclaredField("f"); + F_OFFSET = UNSAFE.objectFieldOffset(fField); + } catch (Exception e) { + throw new RuntimeException(e); + } + INT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(int[].class); + } + + static A notInlinedId(A a) { + return a; + } + + static long lateOffset() { + return F_OFFSET; + } + + static long lateOffsetMinusFour() { + return F_OFFSET - 4; + } + + static long lateOffsetDividedByTwo() { + return F_OFFSET / 2; + } + + static long lateArrayOffset() { + return INT_ARRAY_OFFSET; + } + + static void lateStore(A a) { + a.f = 42; + } + + static void lateArrayStore(int[] a) { + a[0] = 42; + } + + static int lateLoad(A a) { + return a.f; + } + + static Object lateBase(A a) { + return a; + } + + // Test that when lateStore() is inlined, the IGVN-recorded type of the + // accessed memory address (captured by an AddP) has been updated to reflect + // the compiler-known offset discovered by inlining lateOffset(). Failure to + // do so leads to a slice mismatch when parsing the inlined store. + static int testLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(A a) { + long o = lateOffset(); + int val = UNSAFE.getInt(a, o); + lateStore(a); + return val; + } + + // Test that when lateLoad() is inlined, the IGVN-recorded type of the + // accessed memory address (captured by an AddP) has been updated to reflect + // the compiler-known offset discovered by inlining lateOffset(). Failure to + // do so leads to a slice mismatch when parsing the inlined load. + static int testLoadFromLateDiscoveredOffsetThenLoadFromConstOffset(A a) { + long o = lateOffset(); + int val = UNSAFE.getInt(a, o); + lateLoad(a); + return val; + } + + // Test a variation of the above where lateOffsetMinusFour() is not used + // directly by an AddP node. This test does not require updating the + // IGVN-recorded type of the accessed memory address for correctness, + // because lateStore() does not reuse the corresponding AddP node. + static int testLoadFromLateDiscoveredOffsetPlusFourThenStoreAtConstOffset(A a) { + long o = lateOffsetMinusFour(); + int val = UNSAFE.getInt(a, o + 4); + lateStore(a); + return val; + } + + // Test a variation of the above using a different arithmetic operation, + // with the same expectations. + static int testLoadFromLateDiscoveredOffsetTimesTwoThenStoreAtConstOffset(A a) { + long o = lateOffsetDividedByTwo(); + int val = UNSAFE.getInt(a, o * 2); + lateStore(a); + return val; + } + + // Test a variation of the first test where failing to update the + // IGVN-recorded type of the accessed memory address would result in a slice + // mismatch that will lead to an incorrect memory graph (the memory input of + // the last load would bypass the memory output of the store). + static int testLoadFromLateDiscoveredOffsetThenStoreAtConstOffsetThenReloadFromConstOffset(A a) { + A a2 = notInlinedId(a); + long o = lateOffset(); + int val = UNSAFE.getInt(a, o); + lateStore(a); + return a2.f + val; + } + + // Test a variation of the first test where the offset is compiler-known + // from the beginning, but the unsafe base address is only discovered by + // inlining lateBase(). This variation does not require a cleanup between + // the late inlining of lateBase() and lateLoad() for correctness: a slice + // mismatch cannot occur because the memory access within lateLoad() does + // not reuse the same address node (AddP) as the unsafe load. The unsafe + // load address node is not reusable by the lateLoad() access because it is + // obscured by casts by the time lateLoad() is late inlined. Making the + // address node reusable by both loads would require a cleanup round, which + // would prevent the mismatch from happening in the first place. + static int testLoadFromLateDiscoveredBaseThenLoadFromKnownBase(A a) { + Object obj = lateBase(a); + int val = UNSAFE.getInt(obj, F_OFFSET); + lateLoad(a); + return val; + } + + // Test a variation of the first test using an array instead of a class + // instance. No slice mismatch occurs because the address types for both + // memory accesses lead to the same slice, regardless of whether the offset + // is compiler-known. + static int testArrayLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(int[] a) { + long o = lateArrayOffset(); + int val = UNSAFE.getInt(a, o); + lateArrayStore(a); + return val; + } + + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetThenLoadFromConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetPlusFourThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetTimesTwoThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetThenStoreAtConstOffsetThenReloadFromConstOffset(a); + Asserts.assertEquals(42, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredBaseThenLoadFromKnownBase(a); + Asserts.assertEquals(0, result); + } + { + int[] a = new int[1]; + int result = testArrayLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java b/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java index ce9444823da..393d33f62a6 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java @@ -110,7 +110,6 @@ public class TestLongUnsignedDivMod { } @Test // needs to be run in (fast) debug mode - @Warmup(10000) @IR(counts = {IRNode.UDIV_L, ">= 1"}) // At least one UDivL node is generated if intrinsic is used public void testDivideUnsigned() { for (int i = 0; i < BUFFER_SIZE; i++) { @@ -124,7 +123,6 @@ public class TestLongUnsignedDivMod { } @Test // needs to be run in (fast) debug mode - @Warmup(10000) @IR(counts = {IRNode.UMOD_L, ">= 1"}) // At least one UModL node is generated if intrinsic is used public void testRemainderUnsigned() { for (int i = 0; i < BUFFER_SIZE; i++) { @@ -139,7 +137,6 @@ public class TestLongUnsignedDivMod { @Test // needs to be run in (fast) debug mode - @Warmup(10000) @IR(applyIfPlatform = {"x64", "true"}, counts = {IRNode.UDIV_MOD_L, ">= 1"}) // At least one UDivModL node is generated if intrinsic is used public void testDivModUnsigned() { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 07d4ce4f74a..249e73fa54b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2442,6 +2442,12 @@ public class IRNode { vectorNode(VECTOR_BLEND_D, "VectorBlend", TYPE_DOUBLE); } + public static final String VECTOR_BITWISE_BLEND = PREFIX + "VECTOR_BITWISE_BLEND" + POSTFIX; + static { + String regex = START + "VectorBitwiseBlend" + MID + END; + afterBarrierExpansionToBeforeMatching(VECTOR_BITWISE_BLEND, regex); + } + public static final String VECTOR_MASK_CMP_I = VECTOR_PREFIX + "VECTOR_MASK_CMP_I" + POSTFIX; static { vectorNode(VECTOR_MASK_CMP_I, "VectorMaskCmp", TYPE_INT); @@ -2847,6 +2853,36 @@ public class IRNode { machOnlyNameRegex(VSTOREMASK_TRUECOUNT, "vstoremask_truecount_neon"); } + public static final String X86_VMULUDQ_REG = PREFIX + "X86_VMULUDQ_REG" + POSTFIX; + static { + machOnlyNameRegex(X86_VMULUDQ_REG, "vmuludq_reg"); + } + + public static final String X86_VMULDQ_REG = PREFIX + "X86_VMULDQ_REG" + POSTFIX; + static { + machOnlyNameRegex(X86_VMULDQ_REG, "vmuldq_reg"); + } + + public static final String AARCH64_VMULL_UINT_SVE2 = PREFIX + "AARCH64_VMULL_UINT_SVE2" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_UINT_SVE2, "vmulL_uint_sve2"); + } + + public static final String AARCH64_VMULL_INT_SVE2 = PREFIX + "AARCH64_VMULL_INT_SVE2" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_INT_SVE2, "vmulL_int_sve2"); + } + + public static final String AARCH64_VMULL_UINT_NEON = PREFIX + "AARCH64_VMULL_UINT_NEON" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_UINT_NEON, "vmulL_uint_neon"); + } + + public static final String AARCH64_VMULL_INT_NEON = PREFIX + "AARCH64_VMULL_INT_NEON" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_INT_NEON, "vmulL_int_neon"); + } + public static final String X86_SCONV_D2I = PREFIX + "X86_SCONV_D2I" + POSTFIX; static { machOnlyNameRegex(X86_SCONV_D2I, "convD2I_reg_reg"); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java index 33eba66cd8c..5bfa217a1bb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java @@ -33,6 +33,27 @@ import compiler.lib.template_framework.Template; * additional functionality for code generation. These types with their extended * functionality can be used with many other code generation facilities in the * library, such as generating random {@code Expression}s. + * + *

    This module distinguishes scalar Java types and + * Vector API lane-element types: + *

      + *
    • Scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES}/etc. enumerate + * only Java primitive types ({@code byte}, {@code short}, ...). + * These lists are typed as {@code List} and are consumed + * by scalar fuzzers / scalar code generation. {@link Float16Type} (the + * scalar {@code Float16} logical type) is included in + * {@link #SCALAR_NUMERIC_TYPES}.
    • + *
    • Vector-lane lists ({@code VECTOR_ELEMENT_TYPES}, + * {@code FLOATING_VECTOR_ELEMENT_TYPES}, ...) enumerate the lane types + * valid for {@code VectorType.Vector}. These are typed as + * {@code List} and additionally include + * {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16} since {@code Float16Vector} is a real + * Vector API type whose lanes happen to have no Java primitive + * keyword.
    • + *
    + * Vector generators (e.g. {@code Operations.VECTOR_OPERATIONS}) consume the + * vector-lane lists; scalar generators (e.g. + * {@code Operations.PRIMITIVE_OPERATIONS}) consume the scalar lists. */ public interface CodeGenerationDataNameType extends DataName.Type { @@ -101,9 +122,22 @@ public interface CodeGenerationDataNameType extends DataName.Type { static PrimitiveType booleans() { return PrimitiveType.BOOLEANS; } /** - * The Float16 type. + * The {@code short}-carried {@code Float16} lane-element type used by + * {@code Float16Vector}. This is a {@link VectorElementType}, + * not a Java {@link PrimitiveType}; it appears in + * vector-lane lists but never in the scalar + * {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES} lists. Its lanes carry the + * raw bits of the {@code Float16} value in a {@code short}, hence the + * explicit {@code shortCarriesFloat16} naming. * - * @return The Float16 type. + * @return The {@code Float16Vector} {@link VectorElementType}. + */ + static ShortCarriesFloat16Type shortCarriesFloat16() { return ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16; } + + /** + * The {@code Float16} scalar (boxed) type. + * + * @return The scalar {@code Float16} type. */ static CodeGenerationDataNameType float16() { return Float16Type.FLOAT16; } @@ -185,6 +219,61 @@ public interface CodeGenerationDataNameType extends DataName.Type { float16() ); + // -------------------------------------------------------------------- + // Vector API lane-element type lists. + // + // These are typed as List and may include + // ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16 in addition to the Java + // primitive lane carriers. Vector generators (e.g. Operations.VECTOR_OPS) + // iterate over these lists to enumerate the lane types they support. + // -------------------------------------------------------------------- + + /** + * All Vector API lane-element types: every Java numeric primitive lane + * carrier plus {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16}. + */ + List VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + shortCarriesFloat16(), + ints(), + longs(), + floats(), + doubles() + ); + + /** + * Integral Vector API lane-element types (byte, short, int, long). + */ + List INTEGRAL_VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + ints(), + longs() + ); + + /** + * Floating Vector API lane-element types (float16, float, double). + */ + List FLOATING_VECTOR_ELEMENT_TYPES = List.of( + shortCarriesFloat16(), + floats(), + doubles() + ); + + /** + * Vector API lane-element types whose lanes are 32/64 bits and integral + * (int, long). + */ + List INT_LONG_VECTOR_ELEMENT_TYPES = List.of( + ints(), + longs() + ); + + // -------------------------------------------------------------------- + // Concrete VectorType lists (typed as the concrete Vector subclasses). + // -------------------------------------------------------------------- + List VECTOR_BYTE_VECTOR_TYPES = List.of( VectorType.BYTE_64, VectorType.BYTE_128, @@ -199,6 +288,13 @@ public interface CodeGenerationDataNameType extends DataName.Type { VectorType.SHORT_512 ); + List VECTOR_FLOAT16_VECTOR_TYPES = List.of( + VectorType.FLOAT16_64, + VectorType.FLOAT16_128, + VectorType.FLOAT16_256, + VectorType.FLOAT16_512 + ); + List VECTOR_INT_VECTOR_TYPES = List.of( VectorType.INT_64, VectorType.INT_128, @@ -230,6 +326,7 @@ public interface CodeGenerationDataNameType extends DataName.Type { List VECTOR_VECTOR_TYPES = Utils.concat( VECTOR_BYTE_VECTOR_TYPES, VECTOR_SHORT_VECTOR_TYPES, + VECTOR_FLOAT16_VECTOR_TYPES, VECTOR_INT_VECTOR_TYPES, VECTOR_LONG_VECTOR_TYPES, VECTOR_FLOAT_VECTOR_TYPES, diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index becda83a029..e9218101081 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -36,10 +36,15 @@ import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; import static compiler.lib.template_framework.library.Float16Type.FLOAT16; +import static compiler.lib.template_framework.library.ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_VECTOR_ELEMENT_TYPES; /** * This class provides various lists of {@link Expression}s, that represent Java operators or library @@ -264,6 +269,10 @@ public final class Operations { ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); + // ------------ Math ------------- + ops.add(Expression.make(LONGS, "Math.multiplyHigh(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Math.unsignedMultiplyHigh(", LONGS, ", ", LONGS, ")")); + // TODO: Math and other classes. // Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset. @@ -322,8 +331,11 @@ public final class Operations { INTEGRAL_ASSOCIATIVE, // Binary - but only safe for integral reductions TERNARY } - private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { - VOP(String name, VOPType type, List elementTypes) { + // VOP element type pools are typed as VectorElementType so they can include + // ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16 (the Float16Vector lane type) alongside the + // primitive lane types. + private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { + VOP(String name, VOPType type, List elementTypes) { this(name, type, elementTypes, true); } } @@ -333,81 +345,81 @@ public final class Operations { // But if a test is just interested in determinism, they are still // non-deterministic. private static final List VECTOR_OPS = List.of( - new VOP("ABS", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("ACOS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ASHR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ASIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ATAN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ATAN2", VOPType.BINARY, FLOATING_TYPES, false), // 2 ulp - new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_TYPES), - new VOP("CBRT", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_TYPES), - new VOP("COS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("COSH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("DIV", VOPType.BINARY, FLOATING_TYPES), - new VOP("EXP", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_TYPES), - new VOP("EXPM1", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("FMA", VOPType.TERNARY, FLOATING_TYPES), - new VOP("HYPOT", VOPType.BINARY, FLOATING_TYPES, false), // 1.5 ulp - new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("LOG", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LOG10", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LOG1P", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LSHL", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("LSHR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("MIN", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("MAX", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("NEG", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("NOT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("POW", VOPType.BINARY, FLOATING_TYPES, false), // 1 ulp - new VOP("REVERSE", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("ROL", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ROR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SADD", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("SINH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("SQRT", VOPType.UNARY, FLOATING_TYPES), - new VOP("SSUB", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SUADD", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SUB", VOPType.BINARY, PRIMITIVE_TYPES), - new VOP("SUSUB", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("TAN", VOPType.UNARY, FLOATING_TYPES, false), // 1.25 ulp - new VOP("TANH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ZOMO", VOPType.UNARY, INTEGRAL_TYPES) + new VOP("ABS", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("ACOS", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ASHR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ASIN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ATAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ATAN2", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2 ulp + new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("CBRT", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_VECTOR_ELEMENT_TYPES), + new VOP("COS", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("COSH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("DIV", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("EXP", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_VECTOR_ELEMENT_TYPES), + new VOP("EXPM1", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("FMA", VOPType.TERNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("HYPOT", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1.5 ulp + new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("LOG", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LOG10", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LOG1P", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LSHL", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("LSHR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("MIN", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("MAX", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("NEG", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("NOT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("POW", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("REVERSE", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ROL", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ROR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SADD", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SIN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("SINH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("SQRT", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("SSUB", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SUADD", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SUB", VOPType.BINARY, VECTOR_ELEMENT_TYPES), + new VOP("SUSUB", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("TAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1.25 ulp + new VOP("TANH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ZOMO", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES) ); private static final List VECTOR_CMP = List.of( - new VOP("EQ", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("GE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("GT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("LE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("LT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("NE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES) + new VOP("EQ", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("GE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("GT", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("LE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("LT", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("NE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES) ); private static final List VECTOR_TEST = List.of( - new VOP("IS_DEFAULT", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("IS_NEGATIVE", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("IS_FINITE", VOPType.UNARY, FLOATING_TYPES), - new VOP("IS_NAN", VOPType.UNARY, FLOATING_TYPES), - new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_TYPES) + new VOP("IS_DEFAULT", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("IS_NEGATIVE", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("IS_FINITE", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("IS_NAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES) ); // TODO: Conversion VectorOperators -> convertShape @@ -472,14 +484,14 @@ public final class Operations { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))")); + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), 0))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class),", + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class),", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); } @@ -494,14 +506,14 @@ public final class Operations { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))", reinterpretInfo)); + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), 0))", reinterpretInfo)); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class),", + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class),", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); if (type.elementType == BYTES) { @@ -519,6 +531,9 @@ public final class Operations { if (type.elementType == FLOATS) { ops.add(Expression.make(type, "", type2, ".reinterpretAsFloats()", reinterpretInfo)); } + if (type.elementType == SHORT_CARRIES_FLOAT16) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsFloat16s()", reinterpretInfo)); + } if (type.elementType == DOUBLES) { ops.add(Expression.make(type, "", type2, ".reinterpretAsDoubles()", reinterpretInfo)); } @@ -554,8 +569,8 @@ public final class Operations { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); @@ -563,8 +578,8 @@ public final class Operations { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); @@ -581,16 +596,16 @@ public final class Operations { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))", reinterpretInfo)); } else { @@ -600,16 +615,16 @@ public final class Operations { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); } @@ -791,6 +806,27 @@ public final class Operations { // skip hashCode } + // ----------------- ShortCarriesFloat16Type lane bridges -------------------- + // ShortCarriesFloat16Type is the Float16Vector lane type; its lanes carry the raw + // bits of a Float16 in a short. We bridge it both to the boxed Float16 (rich + // float16 arithmetic) and to a plain short (raw-bit fiddling), so expression + // nesting can transition in and out of the lane type and so any IGVN + // optimizations on those transitions are exercised. + var float16Lane = ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16; + // Lane carrier -> boxed Float16: lifts a lane()/reduceLanes() result into the rich + // scalar Float16 world. The raw bits are not exposed (NaN-awareness is handled by + // Float16 verification), so deterministic. + ops.add(Expression.make(FLOAT16, "Float16.shortBitsToFloat16(", float16Lane, ")")); + // Boxed Float16 -> lane carrier: produces a ShortCarriesFloat16Type scalar to feed + // Float16Vector.broadcast/add(scalar)/withLane(...). + ops.add(Expression.make(float16Lane, "Float16.float16ToShortBits(", FLOAT16, ")")); + ops.add(Expression.make(float16Lane, "Float16.float16ToRawShortBits(", FLOAT16, ")")); + // Raw short <-> lane carrier: a Java-level no-op (both are carried in a short), but + // a type-level transition. short -> lane is deterministic; lane -> short exposes the + // raw bits, so distinct NaN encodings make it non-deterministic (preventing result verification). + ops.add(Expression.make(SHORT_CARRIES_FLOAT16, "/*cast to ShortCarriesFloat16Type*/(", SHORTS, ")")); + ops.add(Expression.make(SHORTS, "/*cast to short*/(", SHORT_CARRIES_FLOAT16, ")", WITH_NONDETERMINISTIC_RESULT)); + // TODO: VectorSpecies API methods // Make sure the list is not modifiable. @@ -834,8 +870,18 @@ public final class Operations { FLOAT16_OPERATIONS ); + /** + * Provides a list of Vector API operations. Iterates over all + * {@link CodeGenerationDataNameType#VECTOR_VECTOR_TYPES}, including + * {@code Float16Vector_*}, whose lanes are described by + * {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16}. + */ public static final List VECTOR_OPERATIONS = generateVectorOperations(); + /** + * Provides a list of all operations: every scalar operation and every + * Vector API operation. + */ public static final List ALL_OPERATIONS = Utils.concat( SCALAR_NUMERIC_OPERATIONS, VECTOR_OPERATIONS diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index cd796fd0d31..31e0eecbac3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -40,8 +40,19 @@ import static compiler.lib.template_framework.Template.let; * The {@link PrimitiveType} models Java's primitive types, and provides a set * of useful methods for code generation, such as the {@link #byteSize} and * {@link #boxedTypeName}. + * + *

    {@link PrimitiveType} is a Java scalar type and additionally + * doubles as a {@link VectorElementType} for those Vector API lane types whose + * lane carrier is itself a Java primitive (e.g. {@code IntVector}'s lane + * carrier is {@code int}). For these primitive lane types + * {@link #carrierTypeName} coincides with {@link #name}. + * + *

    Non-primitive lane types, such as the {@code Float16Vector} lane, are + * modeled by separate {@link VectorElementType} implementations (see + * {@link ShortCarriesFloat16Type}). They do not appear in any of + * the scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES} lists. */ -public final class PrimitiveType implements CodeGenerationDataNameType { +public final class PrimitiveType implements VectorElementType { private static final Random RANDOM = Utils.getRandomInstance(); private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE); private static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); @@ -107,6 +118,23 @@ public final class PrimitiveType implements CodeGenerationDataNameType { }; } + @Override + public String carrierTypeName() { + return name(); + } + + @Override + public String vectorElementClass() { + // For primitive lanes the code-usable name and the lane element class + // token coincide (e.g. "int" -> int.class). boolean/char are not real + // Vector API lane element types, so we fail fast during code generation + // rather than emitting code that would only break at compile/runtime. + if (kind == Kind.BOOLEAN || kind == Kind.CHAR) { + throw new UnsupportedOperationException(name() + " is not a Vector API lane element type"); + } + return name(); + } + @Override public String toString() { return name(); @@ -132,6 +160,7 @@ public final class PrimitiveType implements CodeGenerationDataNameType { * @return Size of the type in bytes. * @throws UnsupportedOperationException for boolean which has no defined size. */ + @Override public int byteSize() { return switch (kind) { case BYTE -> 1; @@ -147,6 +176,7 @@ public final class PrimitiveType implements CodeGenerationDataNameType { * * @return the name of the boxed type. */ + @Override public String boxedTypeName() { return switch (kind) { case BYTE -> "Byte"; @@ -194,6 +224,7 @@ public final class PrimitiveType implements CodeGenerationDataNameType { * * @return true iff the type is a floating point type. */ + @Override public boolean isFloating() { return switch (kind) { case BYTE, SHORT, CHAR, INT, LONG, BOOLEAN -> false; @@ -213,6 +244,7 @@ public final class PrimitiveType implements CodeGenerationDataNameType { * @return the token representing the method call to obtain a * random value for the given type at runtime. */ + @Override public Object callLibraryRNG() { return switch (kind) { case BYTE -> "LibraryRNG.nextByte()"; @@ -231,6 +263,12 @@ public final class PrimitiveType implements CodeGenerationDataNameType { * random number generators available, wrapping {@link Generators}. This * is supposed to be used in tandem with {@link #callLibraryRNG}. * + *

    In addition to the Java primitive generators, this also emits + * helpers for {@code Float16Vector}'s {@code short} carrier + * ({@code nextFloat16()} / {@code fill_float16(short[])}) so that + * {@link ShortCarriesFloat16Type#callLibraryRNG()} can be used with vector + * fuzzers without depending on this class importing Float16Vector itself. + * * Note: you must ensure that all required imports are performed: * {@code java.util.Random} * {@code jdk.test.lib.Utils} @@ -250,6 +288,7 @@ public final class PrimitiveType implements CodeGenerationDataNameType { private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); private static final Generator GEN_DOUBLE = Generators.G.doubles(); private static final Generator GEN_FLOAT = Generators.G.floats(); + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); public static byte nextByte() { return GEN_BYTE.next().byteValue(); @@ -283,6 +322,17 @@ public final class PrimitiveType implements CodeGenerationDataNameType { return RANDOM.nextBoolean(); } + // Float16Vector lane helpers. Float16 lanes are carried in short[]. + public static short nextFloat16() { + return GEN_FLOAT16.next(); + } + + public static void fill_float16(short[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = nextFloat16(); + } + } + """, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(type -> scope( let("type", type), diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java new file mode 100644 index 00000000000..33c5535b99b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java @@ -0,0 +1,112 @@ +/* + * 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 compiler.lib.template_framework.library; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; + +import compiler.lib.template_framework.DataName; + +/** + * The {@link ShortCarriesFloat16Type} is the {@link VectorElementType} that describes + * the lane type of a {@code Float16Vector}. Its name makes the semantics + * explicit: a {@code Float16} value carried in a {@code short}. + * + *

    Float16 is not a Java primitive type and therefore does + * not appear in any of the scalar {@link PrimitiveType} lists. As a + * {@link VectorElementType} it appears in vector-lane-typed lists such as + * {@link CodeGenerationDataNameType#VECTOR_ELEMENT_TYPES} and + * {@link CodeGenerationDataNameType#FLOATING_VECTOR_ELEMENT_TYPES}, which are + * consumed by vector-only generators (e.g. {@code Operations.VECTOR_OPERATIONS}). + * + *

    The carrier type for a {@code Float16Vector} lane is {@code short}, so + * {@link #name()} (the code-usable type, per the {@code name()} contract) + * returns {@code "short"}. The logical lane element type token used in + * {@code VectorOperators.Conversion.of*} expressions and + * {@code Float16Vector.SPECIES_*} is {@code Float16}, returned by + * {@link #vectorElementClass()}. + * + *

    NaN handling note: there are multiple bit representations for NaN within + * {@code short}/{@code Float16}. Consumers comparing {@code short[]} carrier + * arrays should canonicalize via {@code Float.float16ToFloat} (which returns a + * canonical NaN) before structural comparison. + */ +public final class ShortCarriesFloat16Type implements VectorElementType { + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); + + /** The singleton instance. */ + public static final ShortCarriesFloat16Type SHORT_CARRIES_FLOAT16 = new ShortCarriesFloat16Type(); + + private ShortCarriesFloat16Type() {} + + @Override + public boolean isSubtypeOf(DataName.Type other) { + return other instanceof ShortCarriesFloat16Type; + } + + @Override + public String name() { + return "short"; + } + + @Override + public String carrierTypeName() { + return "short"; + } + + @Override + public String vectorElementClass() { + return "Float16"; + } + + @Override + public String boxedTypeName() { + return "Float16"; + } + + @Override + public int byteSize() { + return 2; + } + + @Override + public boolean isFloating() { + return true; + } + + @Override + public String toString() { + return name(); + } + + @Override + public Object con() { + return "(short)" + GEN_FLOAT16.next(); + } + + @Override + public Object callLibraryRNG() { + return "LibraryRNG.nextFloat16()"; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java new file mode 100644 index 00000000000..657dc80fd5e --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java @@ -0,0 +1,103 @@ +/* + * 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 compiler.lib.template_framework.library; + +/** + * A {@link VectorElementType} describes a single lane-element of a Vector API + * vector ({@link VectorType.Vector}). It abstracts over: + *

      + *
    • {@link PrimitiveType} - the standard Java primitive lane types + * (byte, short, int, long, float, double). For these {@link #name()} is + * the primitive keyword + * (e.g. {@code "int"}) and {@link #vectorElementClass()} is the same + * token, so {@code vectorElementClass() + ".class"} yields the primitive + * {@code Class} literal ({@code int.class}).
    • + *
    • {@link ShortCarriesFloat16Type} - the {@code Float16Vector} lane type. Float16 + * has no Java primitive keyword; its lanes are stored in a {@code short[]} + * carrier, so {@link #name()} returns the code-usable carrier keyword + * {@code "short"} (consistent with {@link #toString()}), while + * {@link #vectorElementClass()} returns {@code "Float16"} so that + * {@code vectorElementClass() + ".class"} ({@code Float16.class}) is the + * token expected by + * {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret}.
    • + *
    + * + *

    This interface lives outside the scalar + * {@link PrimitiveType} type lists (e.g. {@code PRIMITIVE_TYPES}, + * {@code FLOATING_TYPES}). Those lists model Java scalar types and are consumed + * by scalar fuzzers. Vector-lane lists (e.g. {@code VECTOR_ELEMENT_TYPES}, + * {@code FLOATING_VECTOR_ELEMENT_TYPES}) are typed as {@code List} + * and may include {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16}. + */ +public interface VectorElementType extends CodeGenerationDataNameType { + + /** + * The string whose {@code + ".class"} form is the lane element + * {@code Class} literal expected by the Vector API conversion factories + * ({@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret}) and by + * {@code Float16Vector.SPECIES_*}/{@code IntVector.SPECIES_*} lookups. + * + *

    Unlike {@link #name()} (which must always be a Java type usable + * directly in code, e.g. for variable declarations and casts), this token + * is the logical lane element type. For Java primitive lanes the + * two coincide ({@code "int"} -> {@code int.class}); for {@code Float16} + * lanes {@link #name()} is the carrier {@code "short"} while this returns + * {@code "Float16"} ({@code Float16.class}). + * + * @return The logical lane element type token (e.g. {@code "int"}, + * {@code "float"}, {@code "Float16"}). + */ + String vectorElementClass(); + + /** + * @return The element type of the Java carrier array used to hold these + * lanes when calling {@code fromArray}/{@code intoArray}. For most + * lane types this is the same as {@link #name()}; for + * {@code Float16} it is {@code "short"}. + */ + String carrierTypeName(); + + /** + * @return The boxed type name used to parameterize generic types such as + * {@code VectorMask} and {@code VectorShuffle} + * (e.g. {@code "Integer"}, {@code "Float16"}). + */ + String boxedTypeName(); + + /** + * @return Size of the lane type in bytes. + */ + int byteSize(); + + /** + * @return {@code true} iff the lane type is a floating point type. + */ + boolean isFloating(); + + /** + * @return A token representing a call to the corresponding pseudo random + * number generator from {@link PrimitiveType#generateLibraryRNG()}. + */ + Object callLibraryRNG(); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java index 7eabd42a723..df1365a3566 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java @@ -39,6 +39,11 @@ import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; /** * The {@link VectorType} models the Vector API types. + * + *

    A {@code VectorType.Vector} is parameterized by a {@link VectorElementType} + * (its lane element type) and a lane count. The lane element type may be a + * Java primitive lane ({@link PrimitiveType}) or {@link ShortCarriesFloat16Type} for + * {@code Float16Vector}. */ public abstract class VectorType implements CodeGenerationDataNameType { private static final Random RANDOM = Utils.getRandomInstance(); @@ -73,6 +78,11 @@ public abstract class VectorType implements CodeGenerationDataNameType { public static final VectorType.Vector DOUBLE_256 = new VectorType.Vector(DOUBLES, 4); public static final VectorType.Vector DOUBLE_512 = new VectorType.Vector(DOUBLES, 8); + public static final VectorType.Vector FLOAT16_64 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 4); + public static final VectorType.Vector FLOAT16_128 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 8); + public static final VectorType.Vector FLOAT16_256 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 16); + public static final VectorType.Vector FLOAT16_512 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 32); + private final String vectorTypeName; private VectorType(String vectorTypeName) { @@ -95,8 +105,8 @@ public abstract class VectorType implements CodeGenerationDataNameType { return this == other; } - private static final String vectorTypeName(PrimitiveType elementType) { - return switch(elementType.name()) { + private static final String vectorTypeName(VectorElementType elementType) { + return switch(elementType.vectorElementClass()) { case "byte" -> "ByteVector"; case "short" -> "ShortVector"; case "char" -> throw new UnsupportedOperationException("VectorAPI has no char vector type"); @@ -104,19 +114,20 @@ public abstract class VectorType implements CodeGenerationDataNameType { case "long" -> "LongVector"; case "float" -> "FloatVector"; case "double" -> "DoubleVector"; - default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); + case "Float16" -> "Float16Vector"; + default -> throw new UnsupportedOperationException("Not supported: " + elementType.vectorElementClass()); }; } public static final class Vector extends VectorType { - public final PrimitiveType elementType; + public final VectorElementType elementType; public final int length; // lane count public final String speciesName; public final Mask maskType; public final Shuffle shuffleType; - private Vector(PrimitiveType elementType, int length) { + private Vector(VectorElementType elementType, int length) { super(vectorTypeName(elementType)); this.elementType = elementType; this.length = length; @@ -132,7 +143,7 @@ public abstract class VectorType implements CodeGenerationDataNameType { return List.of(name(), ".zero(", speciesName, ")"); } else if (r <= 8) { return List.of( - name(), ".fromArray(", speciesName, ", new ", elementType.name(), "[] {", + name(), ".fromArray(", speciesName, ", new ", elementType.carrierTypeName(), "[] {", elementType.con(), Stream.generate(() -> List.of(", ", elementType.con()) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index c79ad2c55a0..32463bf3454 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -52,6 +52,11 @@ import java.util.HashMap; * This applies to the boxed floating types, as well as arrays of floating arrays. With * {@link Verify#checkEQWithRawBits} we compare the raw bits, and so different NaN encodings are not equal. * Note: {@link MemorySegment} data is always compared with raw bits. + * + *

    + * The same NaN handling applies to {@code Float16}: both the scalar {@code Float16} box and the + * {@code Float16Vector} lanes (whose {@code short} carrier bits encode Float16 values) are compared + * with the selected NaN mode rather than as raw {@code short}s. */ public final class Verify { private final boolean isFloatCheckWithRawBits; @@ -455,9 +460,58 @@ public final class Verify { } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); } + // A Float16Vector carries its lanes in a short[], but those short bits encode Float16 + // values rather than plain shorts. Comparing them as a raw short[] would treat distinct + // NaN encodings as unequal, even in the non-raw mode. Compare them with Float16 NaN + // semantics instead. + if (va instanceof short[] sa && vb instanceof short[] sb && isFloat16VectorClass(ca)) { + checkEQForFloat16Carrier(sa, sb, field + ".toArray", aParent, bParent); + return; + } checkEQdispatch(va, vb, field + ".toArray", aParent, bParent); } + private static boolean isFloat16VectorClass(Class c) { + // The concrete classes (Float16Vector64/128/256/512/Max) all extend Float16Vector. + for (Class k = c; k != null; k = k.getSuperclass()) { + if (k.getName().equals("jdk.incubator.vector.Float16Vector")) { + return true; + } + } + return false; + } + + /** + * Compare the {@code short[]} carriers of two {@code Float16Vector}s. The short bits encode + * Float16 values, so in the non-raw mode we canonicalize NaN by widening each lane to float + * via {@link Float#float16ToFloat}, and then reuse the float canonicalization. In the raw mode we + * compare the carrier bits directly, so distinct NaN encodings are not equal. See {@link #isFloatEQ}. + */ + private void checkEQForFloat16Carrier(short[] a, short[] b, String field, Object aParent, Object bParent) { + if (a.length != b.length) { + System.err.println("ERROR: Equality matching failed: length mismatch: " + a.length + " vs " + b.length); + print(a, b, field, aParent, bParent); + throw new VerifyException("Float16 array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (!isFloat16EQ(a[i], b[i])) { + System.err.println("ERROR: Equality matching failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); + print(a, b, field, aParent, bParent); + throw new VerifyException("Float16 array value mismatch " + a[i] + " vs " + b[i]); + } + } + } + + /** + * For Float16 we widen each lane to float, which is exact and lossless and maps every NaN encoding + * to the canonical float NaN, and then reuse the float canonicalization. + */ + private boolean isFloat16EQ(short a, short b) { + return isFloatCheckWithRawBits ? a == b + : Float.floatToIntBits(Float.float16ToFloat(a)) == Float.floatToIntBits(Float.float16ToFloat(b)); + } + private static boolean isFloat16Class(Class c) { return c.getName().equals("jdk.incubator.vector.Float16"); } diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java b/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java index 7e55353e0f7..ed65deb6c85 100644 --- a/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java @@ -32,14 +32,11 @@ import java.util.Objects; * @bug 8342692 * @summary C2: long counted loop/long range checks: don't create loop-nest for short running loops * @library /test/lib / - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.longcountedloops.TestShortRunningLongCountedLoop + * @run driver ${test.main.class} */ public class TestShortRunningLongCountedLoop { private static volatile int volatileField; - private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { // IR rules expect a single loop so disable unrolling @@ -351,8 +348,9 @@ public class TestShortRunningLongCountedLoop { throw new RuntimeException("incorrect result: " + res); } } - wb.enqueueMethodForCompilation(info.getTest(), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); - if (!wb.isMethodCompiled(info.getTest())) { + WhiteBox whitebox = WhiteBox.getWhiteBox(); + whitebox.enqueueMethodForCompilation(info.getTest(), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!whitebox.isMethodCompiled(info.getTest())) { throw new RuntimeException("Should be compiled now"); } for (int i = 0; i < 10; i++) { diff --git a/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java b/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java index d8b6fc3fdfb..9375e7b7f40 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,8 @@ * @test * @bug 8273115 * @summary CountedLoopEndNode::stride_con crash in debug build with -XX:+TraceLoopOpts - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceLoopOpts -Xcomp -XX:-TieredCompilation + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+TraceLoopOpts -Xcomp -XX:-TieredCompilation * -XX:CompileOnly=TestBadlyFormedCountedLoop::main TestBadlyFormedCountedLoop */ diff --git a/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java b/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java index 3b2c82afc46..10f2e0c113a 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -39,7 +39,7 @@ * @key stress randomness * @bug 8299975 * @summary Limit underflow protection CMoveINode in PhaseIdealLoop::do_unroll must also protect type from underflow - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput -XX:-TieredCompilation * -XX:CompileCommand=compileonly,compiler.loopopts.TestCMoveLimitType::test* * -XX:CompileCommand=dontinline,compiler.loopopts.TestCMoveLimitType::dontInline * -XX:RepeatCompilation=50 -XX:+StressIGVN diff --git a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java new file mode 100644 index 00000000000..143933ed6ea --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java @@ -0,0 +1,1142 @@ +/* + * 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 id=vanilla + * @bug 8385855 + * @summary Test CountedLoopConverter::has_truncation_wrap logic that checks if + * a truncated iv (e.g. byte or char iv) is still a valid counted loop. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +/* + * @test id=Xcomp + * @bug 8385855 + * @library /test/lib / + * @run driver ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + */ + +package compiler.loopopts; + +import compiler.lib.ir_framework.*; + +/** + * Tests for CountedLoopConverter::has_truncation_wrap, which deals with wrapped iv, for byte/char/short iv cases. + * We have some regression tests for JDK-8385855, as well as some IR tests that ensure that we detect counted + * loops in many cases, where we have to check that truncation does not lead to wrapping, which would mean + * the iv would not be linear, but possibly overflow the byte/char/short ranges. + * + * Note: the optimization around CountedLoopConverter::has_truncation_wrap is a bit fragile, and depends on + * the exact loop shape, and if peeling happens or not, etc. The goal of this test is not to prove that we + * recognize all truncated cases where one could in theory prove there is no wrap/overflow, but simply to + * list some examples of today's state, so we don't get further regressions in the future. + */ +public class TestHasTruncationWrap { + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + framework.addFlags(args); + framework.start(); + } + + // ------------------------- Failing cases for JDK-8385855 ------------------------------ + + // Test shape first reported in JDK-8385855, led to assert in JDK27: + // assert(cmp->Opcode() == Op_CmpI) failed: signed comparison required + public static int test0_start = 0; + public static int test0_stop = 100; + public static int[] test0_array = new int[100]; + + @Test + public static void test0() { + int start = test0_start; + int stop = test0_stop; + int[] array = test0_array; + + stop = (stop << 16) >> 16; + int v = array[start]; // dominating CmpU detected by filtered_int_type + for (int i = start; i < stop;) { + i++; + i = (i << 16) >> 16; // iv truncation + } + } + + // A second reproducer from JDK-8385855, leads to wrong result since JDK18 (JDK-8276162). + // We make use of the CmpU via Integer.compareUnsigned, introduced by JDK-8276162. + public static int test1_gold0 = 32767; // test1(-2); + public static int test1_gold1 = 3; // test1(2); + + @Run(test = "test1") + private static void run1() { + int val0 = test1(-2); + int val1 = test1( 2); + if (val0 != test1_gold0) { throw new RuntimeException("wrong value test(-2): " + test1_gold0 + " vs " + val0); } + if (val1 != test1_gold1) { throw new RuntimeException("wrong value test( 2): " + test1_gold1 + " vs " + val1); } + } + + @Test + private static int test1(int start) { + // CmpU Condition: start =u 2 + // But filtered_int_type mistakes it as a CmpI. + // Bad CmpU assumption: start >= 2 + + int i = start; + while (i < 3) { + // While condition: i <= 2 + + // char-truncation of iv: has_truncation_wrap + // We try to see if the char-truncation can be removed. + // + // Computing loop entry type: + // While condition: i <= 2 + // Bad assumption from CmpU: start >= 2 + // -> entry type i = 2 + // + // Together with the backedge type, we get the complete phi type: + // i in [1..2] + // + // The truncation below would be a no-op for input ranges [0 .. 32767]. + // Since [1..2] is a subrange: remove truncation! + // + // But: the correct CmpU assumption would only be: + // start >=u 2 + // And that allows almost all values (except 0 and 1), in particular + // it allows the whole negative int range. + // And the while condition also allows all negative ints. + // And for negative ints, the truncation is NOT a no-op. + i = (i + 1) & 0x7fff; + + // Continuing after the backedge would mean: + // i >= 1 + // Together with while condition: + // i <= 2 + // We get a backedge type: + // i in [1..2] + if (i < 1) { + break; + } + } + return i; + } + + // A third reproducer from JDK-8385855, leads to wrong result since 6u. + // We make use of the CmpU in the RangeCheck of an array access. + // To flip the condition, we just use a try/catch. + public static final int[] test2_A = new int[2]; + public static int test2_gold0 = 32767; // test2(-2); + public static int test2_gold1 = 3; // test2(2); + + @Run(test = "test2") + private static void run2() { + int val0 = test2(-2); + int val1 = test2( 2); + if (val0 != test2_gold0) { throw new RuntimeException("wrong value test(-2): " + test2_gold0 + " vs " + val0); } + if (val1 != test2_gold1) { throw new RuntimeException("wrong value test( 2): " + test2_gold1 + " vs " + val1); } + } + + @Test + static int test2(int start) { + try { + // CmpU Condition: start =u A.length = 2 + int i = start; + while (i < 3) { + // Truncating induction-variable update. + i = (i + 1) & 0x7fff; + if (i < 1) { + break; + } + } + return i; + } + } + + // ---- More general tests, Checking that truncated iv loops become CountedLoops --------- + + @DontInline + public static int opaqueSum(int i) { + return i + 1; + } + + @DontInline + public static int opaqueSum(int i, int j) { + return i + j + 1; + } + + public static int lo = 11; + public static int hi = 33; + + // testIRShort0: just a regular int loop + public static int testIRShort0_gold = testIRShort0(); + + @Run(test = "testIRShort0") + private static void runIRShort0() { + int val = testIRShort0(); + if (val != testIRShort0_gold) { throw new RuntimeException("wrong value: " + testIRShort0_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort0() { + int init = lo; + int limit = hi; + int sum = 0; + for (int i = init; i < limit; i++) { + sum = opaqueSum(sum); + } + return sum; + } + + // testIRShort0b: just a regular int loop, but with NEQ exit check. + public static int testIRShort0b_gold = testIRShort0b(); + + @Run(test = "testIRShort0b") + private static void runIRShort0b() { + int val = testIRShort0b(); + if (val != testIRShort0b_gold) { throw new RuntimeException("wrong value: " + testIRShort0b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort0b() { + int init = lo; + int limit = hi; + int sum = 0; + for (int i = init; i != limit; i++) { + sum = opaqueSum(sum); + } + return sum; + } + + // testIRShort1: short loop, but values are trivially in short range. + public static int testIRShort1_gold = testIRShort1(); + + @Run(test = "testIRShort1") + private static void runIRShort1() { + int val = testIRShort1(); + if (val != testIRShort1_gold) { throw new RuntimeException("wrong value: " + testIRShort1_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort1() { + short init = (short)lo; + short limit = (short)hi; + int sum = 0; + for (short i = init; i < limit; i++) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort1b: short loop, but values are trivially in short range. Decrement iv. + public static int testIRShort1b_gold = testIRShort1b(); + + @Run(test = "testIRShort1b") + private static void runIRShort1b() { + int val = testIRShort1b(); + if (val != testIRShort1b_gold) { throw new RuntimeException("wrong value: " + testIRShort1b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort1b() { + short init = (short)hi; + short limit = (short)lo; + int sum = 0; + for (short i = init; i > limit; i--) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort1c: short loop, but values are trivially in short range. Incr by 2. + // Not safe: lo=32766+2 would wrap past short_max. + public static int testIRShort1c_gold = testIRShort1c(); + + @Run(test = "testIRShort1c") + private static void runIRShort1c() { + int val = testIRShort1c(); + if (val != testIRShort1c_gold) { throw new RuntimeException("wrong value: " + testIRShort1c_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort1c() { + short init = (short)lo; + short limit = (short)hi; + int sum = 0; + for (short i = init; i < limit; i+=2) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort1d: short loop, but values are trivially in short range. Decrement iv by 2. + // Not safe: lo=-32767-2 would wrap past short_min. + public static int testIRShort1d_gold = testIRShort1d(); + + @Run(test = "testIRShort1d") + private static void runIRShort1d() { + int val = testIRShort1d(); + if (val != testIRShort1d_gold) { throw new RuntimeException("wrong value: " + testIRShort1d_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort1d() { + short init = (short)hi; + short limit = (short)lo; + int sum = 0; + for (short i = init; i > limit; i-=2) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort2: short loop, ranges proved in short range via CmpI before loop. + public static int testIRShort2_gold = testIRShort2(); + + @Run(test = "testIRShort2") + private static void runIRShort2() { + int val = testIRShort2(); + if (val != testIRShort2_gold) { throw new RuntimeException("wrong value: " + testIRShort2_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort2() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + int sum = 0; + for (int i = init; i < limit; i = (short)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // The backedge value of i is also far + // enough from short boundaries, because of + // the loop exit check: + // i < limit <= 100 + } + return sum; + } + + // testIRShort2b: short loop, ranges proved in short range via CmpI before loop. + // Compared to testIRShort2, the check in the loop is an NEQ. + // + // Since the bug fix of JDK-8386830, we no longer allow this case to detect CountedLoop: + // The backedge finds no useful constraint, the "i != limit" does not give any restrictions, + // and so we have to assume it produces the full range. + // Comparing with testIRShort2, there we have a useful check "i < limit", which does + // give us a restriction, that helps us prove there is not wrap overflow. + // + // In the future, we could try to do something more smart, and combine the info about + // entry type "init < limit <= 100" with the fact that we have unity-stride, and so + // we should not be able to skip the NEQ "i != limit", and be able to canonicalize + // NEQ to LT. But for now, I consider this an edge-case that we will just have to accept + // will not be optimized to CountedLoop for now. For now, a workaround is using the + // exit condition "i < limit". + // This is really a problem about iv evolution (iv starts in range, increments by 1, + // and cannot skip exit check, so NEQ can be converted to LT), and cannot be solved + // by the type info of entry/backedge separately, so I don't have a quick fix here. + // We do this NEQ to LT canonicalization for int loops, but we would also need + // dedicated logic for it specifically combined with the wrap-detection logic. + public static int testIRShort2b_gold = testIRShort2b(); + + @Run(test = "testIRShort2b") + private static void runIRShort2b() { + int val = testIRShort2b(); + if (val != testIRShort2b_gold) { throw new RuntimeException("wrong value: " + testIRShort2b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort2b() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + int sum = 0; + for (int i = init; i != limit; i = (short)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // Unfortunately, the backedge does not produce a useful + // check with "i != limit", and so the type is unconstrained. + } + return sum; + } + + // testIRShort3: short loop, and range in short range via CmpI before loop (for loop limit). + public static int testIRShort3_gold = testIRShort3(); + + @Run(test = "testIRShort3") + private static void runIRShort3() { + int val = testIRShort3(); + if (val != testIRShort3_gold) { throw new RuntimeException("wrong value: " + testIRShort3_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort3() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + int sum = 0; + // While there is no explicit CmpI before the loop, we + // actually have "i < limit" in the for loop check, which + // is also checked before entering the loop. + // So also here, we have: + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + for (int i = init; i < limit; i = (short)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort3b: short loop, and range in short range via CmpI before loop (for loop limit). + // Decr iv. + // Missed optimization opportunity: + // CountedLoopConverter::LoopStructure::is_infinite_loop + // It wrongly fires, and prevents CountedLoop detection. + // This check is increment-specific, and fails to acocunt for decrement: + // if (limit_t->hi_as_long() > incr_t->hi_as_long()) { + // I don't think this is intentional, because we have handling for positive and + // negative stride in CountedLoopConverter::has_truncation_wrap. + public static int testIRShort3b_gold = testIRShort3b(); + + @Run(test = "testIRShort3b") + private static void runIRShort3b() { + int val = testIRShort3b(); + if (val != testIRShort3b_gold) { throw new RuntimeException("wrong value: " + testIRShort3b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort3b() { + int limit = Math.max(lo, 0); // limit in [0..max_int] + int init = Math.min(hi, 100); // init in [min_int..100] + int sum = 0; + for (int i = init; i > limit; i = (short)(i-1)) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort3x: short loop, fails to be recognized as CountedLoop. + // Compared to testIRShort3, the check in the loop is an NEQ. + public static int testIRShort3x_gold = testIRShort3x(); + + @Run(test = "testIRShort3x") + private static void runIRShort3x() { + int val = testIRShort3x(); + if (val != testIRShort3x_gold) { throw new RuntimeException("wrong value: " + testIRShort3x_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort3x() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + int sum = 0; + // No useful CmpI before the loop. + // And the CmpI of the for limit is NEQ, so not useful either. + for (int i = init; i != limit; i = (short)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShort4: short loop, with a CmpI, but the limit ranges are bad. + public static int testIRShort4_gold = testIRShort4(); + + @Run(test = "testIRShort4") + private static void runIRShort4() { + int val = testIRShort4(); + if (val != testIRShort4_gold) { throw new RuntimeException("wrong value: " + testIRShort4_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort4() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100_000); // limit in [min_int..100_000] + int sum = 0; + // Now, the check is not good enough: + // -> init < limit <= 100_000 + // -> filtered_int_type return [min_int..99_999] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99_999], which is NOT in short range. + for (int i = init; i < limit; i = (short)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // Also: the backedge range is not good because + // the exit check is not strong enough for short: + // i < limit <= 100_000 + } + return sum; + } + + // testIRShort5: short do-while-loop, and range in short range via CmpI before loop (for loop limit). + public static int testIRShort5_gold = testIRShort5(); + + @Run(test = "testIRShort5") + private static void runIRShort5() { + int val = testIRShort5(); + if (val != testIRShort5_gold) { throw new RuntimeException("wrong value: " + testIRShort5_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort5() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + int sum = 0; + int i = init; + do { + sum = opaqueSum(sum); // work to keep loop alive + i = (short)(i+1); + } while (i < limit); // exit check at the end. + return sum; + } + + // testIRShort5b: short do-while-loop, but the backedge check with NEQ is not strong enough to prevent wrapping. + // Compared to testIRShort5, the check in the loop is an NEQ. + public static int testIRShort5b_gold = testIRShort5b(); + + @Run(test = "testIRShort5b") + private static void runIRShort5b() { + int val = testIRShort5b(); + if (val != testIRShort5b_gold) { throw new RuntimeException("wrong value: " + testIRShort5b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort5b() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + int sum = 0; + int i = init; + do { + sum = opaqueSum(sum); // work to keep loop alive + i = (short)(i+1); + } while (i != limit); // exit check at the end, but with NEQ. + return sum; + } + + // testIRShort5c: short do-while-loop. + // While the code shape looks very close to testIRShort2b, it does not behave the same. + // The while loop below is peeled once. The additional "exit check" is eliminated, + // because redundant after "init >= limit" check. + // From peeling, the new initial value is a truncated short value, and not init, so + // the "init >= limit" check is not helpful any more, as far as I can see. + // Also the backedge value is truncated to short value. But this is not enough to + // guarantee that there is no short-overflow (wrap): we do not manage to + // prove that i could never be short_max, and then overflow the short range at + // the next increment. + public static int testIRShort5c_gold = testIRShort5c(); + + @Run(test = "testIRShort5c") + private static void runIRShort5c() { + int val = testIRShort5c(); + if (val != testIRShort5c_gold) { throw new RuntimeException("wrong value: " + testIRShort5c_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort5c() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + int sum = 0; + int i = init; + if (i == limit) { return sum; } // additional "exit check" before loop. + do { + sum = opaqueSum(sum); // work to keep loop alive + i = (short)(i+1); + } while (i != limit); // exit check at the end, but with NEQ. + return sum; + } + + // testIRShort5d: short while-loop, again similar to testIRShort2b and testIRShort5c, but with while-loop form. + // + // Same issue as with testIRShort2b: + // After JDK-8386830, we now see that the backedge type is not constrained, + // and so don't allow CountedLoop detection. + // However, we could be smarter in the future, and canonicalize NEQ + // to LT, because this is a unity-stride loop where the "i != limit" + // can provably not be skipped. For now, we just have to accept that + // we cannot optimize this, and people would have to use "i < limit", + // see testIRShort5. + public static int testIRShort5d_gold = testIRShort5d(); + + @Run(test = "testIRShort5d") + private static void runIRShort5d() { + int val = testIRShort5d(); + if (val != testIRShort5d_gold) { throw new RuntimeException("wrong value: " + testIRShort5d_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort5d() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + int sum = 0; + int i = init; + while (i != limit) { + sum = opaqueSum(sum); // work to keep loop alive + i = (short)(i+1); + // Unfortunately, the backedge does not produce a useful + // check with "i != limit", and so the type is unconstrained. + } + return sum; + } + + // testIRShort6: short do-while-loop, missing the CmpI before the loop. + public static int testIRShort6_gold = testIRShort6(); + + @Run(test = "testIRShort6") + private static void runIRShort6() { + int val = testIRShort6(); + if (val != testIRShort6_gold) { throw new RuntimeException("wrong value: " + testIRShort6_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort6() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + // No CmpI before the loop! + // But the loop exit check is strong enough to ignore truncation. + int sum = 0; + int i = init; + do { + sum = opaqueSum(sum); // work to keep loop alive + i = (short)(i+1); + } while (i < limit); // exit check at the end. + return sum; + } + + // testIRShort6b: short do-while-loop, missing the CmpI before the loop. + // Compared to testIRShort6, the check in the loop is an NEQ. + public static int testIRShort6b_gold = testIRShort6b(); + + @Run(test = "testIRShort6b") + private static void runIRShort6b() { + int val = testIRShort6b(); + if (val != testIRShort6b_gold) { throw new RuntimeException("wrong value: " + testIRShort6b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort6b() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + // No CmpI before the loop! + // And the loop exit check is NOT strong enough to ignore truncation. + int sum = 0; + int i = init; + do { + sum = opaqueSum(sum); // work to keep loop alive + i = (short)(i+1); + } while (i != limit); // exit check at the end. + return sum; + } + + public static int opaqueCounter; + + @DontInline + public static void opaqueReset() { + opaqueCounter = 0; + } + + @DontInline + public static boolean opaqueCheck() { + return (opaqueCounter++) >= 100_000; + } + + // testIRShort7: with additional opaque exit check. + // Useful to verify that TestTruncationWrapFuzzer.java opaque exit checks + // do not prohibit CountedLoop detection. + // We start from testIRShort3 and testIRShort4, but add the additional opaque exit. + public static int testIRShort7_gold = testIRShort7(); + + @Run(test = "testIRShort7") + private static void runIRShort7() { + int val = testIRShort7(); + if (val != testIRShort7_gold) { throw new RuntimeException("wrong value: " + testIRShort7_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShort7() { + opaqueReset(); + int init = Math.max(lo, 0); + int limit = Math.min(hi, 100); // good bounds + int sum = 0; + for (int i = init; i < limit; i = (short)(i+1)) { + sum = opaqueSum(sum, i); + if (opaqueCheck()) { break; } + } + return sum; + } + + // testIRShort7b + public static int testIRShort7b_gold = testIRShort7b(); + + @Run(test = "testIRShort7b") + private static void runIRShort7b() { + int val = testIRShort7b(); + if (val != testIRShort7b_gold) { throw new RuntimeException("wrong value: " + testIRShort7b_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShort7b() { + opaqueReset(); + int init = Math.max(lo, 0); + int limit = Math.min(hi, 100_000); // bad bounds + int sum = 0; + for (int i = init; i < limit; i = (short)(i+1)) { + sum = opaqueSum(sum, i); + if (opaqueCheck()) { break; } + } + return sum; + } + + // testIRByte1: byte loop, but values are trivially in byte range. + // But: "byte i++" goes through "<< 24 >> 24" truncation with signed extension, + // and that's not recognized by TruncatedIncrement::build. + public static int testIRByte1_gold = testIRByte1(); + + @Run(test = "testIRByte1") + private static void runIRByte1() { + int val = testIRByte1(); + if (val != testIRByte1_gold) { throw new RuntimeException("wrong value: " + testIRByte1_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRByte1() { + byte init = (byte)lo; + byte limit = (byte)hi; + int sum = 0; + for (byte i = init; i < limit; i++) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRByte2: byte loop, ranges proved in byte range via CmpI before loop. + // But: "byte i++" goes through "<< 24 >> 24" truncation with signed extension, + // and that's not recognized by TruncatedIncrement::build. + public static int testIRByte2_gold = testIRByte2(); + + @Run(test = "testIRByte2") + private static void runIRByte2() { + int val = testIRByte2(); + if (val != testIRByte2_gold) { throw new RuntimeException("wrong value: " + testIRByte2_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRByte2() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in byte range. + int sum = 0; + for (int i = init; i < limit; i = (byte)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // The backedge value of i is also far + // enough from byte boundaries, because of + // the loop exit check: + // i < limit <= 100 + } + return sum; + } + + // testIRByte4: byte loop, with a CmpI, but the limit ranges are bad. + // And: "byte i++" goes through "<< 24 >> 24" truncation with signed extension, + // and that's not recognized by TruncatedIncrement::build. + public static int testIRByte4_gold = testIRByte4(); + + @Run(test = "testIRByte4") + private static void runIRByte4() { + int val = testIRByte4(); + if (val != testIRByte4_gold) { throw new RuntimeException("wrong value: " + testIRByte4_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRByte4() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 1_000); // limit in [min_int..1_000] + int sum = 0; + // Now, the check is not good enough: + // -> init < limit <= 1_000 + // -> filtered_int_type return [min_int..999] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..999], which is NOT in byte range. + for (int i = init; i < limit; i = (byte)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // Also: the backedge range is not good because + // the exit check is not strong enough for byte: + // i < limit <= 1_000 + } + return sum; + } + + // testIRChar1: char loop, but values are trivially in char range. + // But: "char i++" lowers through mask "& 0xffff", not recognized by TruncatedIncrement::build. + public static int testIRChar1_gold = testIRChar1(); + + @Run(test = "testIRChar1") + private static void runIRChar1() { + int val = testIRChar1(); + if (val != testIRChar1_gold) { throw new RuntimeException("wrong value: " + testIRChar1_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRChar1() { + char init = (char)lo; + char limit = (char)hi; + int sum = 0; + for (char i = init; i < limit; i++) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRChar2: char loop, ranges proved in char range via CmpI before loop. + // But: "char i++" lowers through mask "& 0xffff", not recognized by TruncatedIncrement::build. + public static int testIRChar2_gold = testIRChar2(); + + @Run(test = "testIRChar2") + private static void runIRChar2() { + int val = testIRChar2(); + if (val != testIRChar2_gold) { throw new RuntimeException("wrong value: " + testIRChar2_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRChar2() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + if (init >= limit) { return -1; } // CmpI before loop + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in char range. + int sum = 0; + for (int i = init; i < limit; i = (char)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // The backedge value of i is also far + // enough from char boundaries, because of + // the loop exit check: + // i < limit <= 100 + } + return sum; + } + + // testIRChar3: char loop, and range in char range via CmpI before loop (for loop limit). + // But: "char i++" lowers through mask "& 0xffff", not recognized by TruncatedIncrement::build. + public static int testIRChar3_gold = testIRChar3(); + + @Run(test = "testIRChar3") + private static void runIRChar3() { + int val = testIRChar3(); + if (val != testIRChar3_gold) { throw new RuntimeException("wrong value: " + testIRChar3_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRChar3() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + int sum = 0; + // While there is no explicit CmpI before the loop, we + // actually have "i < limit" in the for loop check, which + // is also checked before entering the loop. + // So also here, we have: + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in char range. + for (int i = init; i < limit; i = (char)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRChar3Mask: char loop, and range in char range via CmpI before loop (for loop limit). + public static int testIRChar3Mask_gold = testIRChar3Mask(); + + @Run(test = "testIRChar3Mask") + private static void runIRChar3Mask() { + int val = testIRChar3Mask(); + if (val != testIRChar3Mask_gold) { throw new RuntimeException("wrong value: " + testIRChar3Mask_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRChar3Mask() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + int sum = 0; + // While there is no explicit CmpI before the loop, we + // actually have "i < limit" in the for loop check, which + // is also checked before entering the loop. + // So also here, we have: + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in char range. + for (int i = init; i < limit; i = (i+1) & 0x7fff) { // mask instead of cast + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRChar4: char loop, with a CmpI, but the limit ranges are bad. + // And: "char i++" lowers through mask "& 0xffff", not recognized by TruncatedIncrement::build. + public static int testIRChar4_gold = testIRChar4(); + + @Run(test = "testIRChar4") + private static void runIRChar4() { + int val = testIRChar4(); + if (val != testIRChar4_gold) { throw new RuntimeException("wrong value: " + testIRChar4_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRChar4() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100_000); // limit in [min_int..100_000] + int sum = 0; + // Now, the check is not good enough: + // -> init < limit <= 100_000 + // -> filtered_int_type return [min_int..99_999] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99_999], which is NOT in char range. + for (int i = init; i < limit; i = (char)(i+1)) { + sum = opaqueSum(sum); // work to keep loop alive + // Also: the backedge range is not good because + // the exit check is not strong enough for char: + // i < limit <= 100_000 + } + return sum; + } + + // testIRChar4Mask: char loop, with a CmpI, but the limit ranges are bad. + public static int testIRChar4Mask_gold = testIRChar4Mask(); + + @Run(test = "testIRChar4Mask") + private static void runIRChar4Mask() { + int val = testIRChar4Mask(); + if (val != testIRChar4Mask_gold) { throw new RuntimeException("wrong value: " + testIRChar4Mask_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRChar4Mask() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100_000); // limit in [min_int..100_000] + int sum = 0; + // Now, the check is not good enough: + // -> init < limit <= 100_000 + // -> filtered_int_type return [min_int..99_999] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99_999], which is NOT in char range. + for (int i = init; i < limit; i = (i+1) & 0x7fff) { // mask instead of cast + sum = opaqueSum(sum); // work to keep loop alive + // Also: the backedge range is not good because + // the exit check is not strong enough for char: + // i < limit <= 100_000 + } + return sum; + } + + // testIRShift16: short loop, and range in short range via CmpI before loop (for loop limit). + public static int testIRShift16_gold = testIRShift16(); + + @Run(test = "testIRShift16") + private static void runIRShift16() { + int val = testIRShift16(); + if (val != testIRShift16_gold) { throw new RuntimeException("wrong value: " + testIRShift16_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + static int testIRShift16() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + int sum = 0; + // While there is no explicit CmpI before the loop, we + // actually have "i < limit" in the for loop check, which + // is also checked before entering the loop. + // So also here, we have: + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in short range. + for (int i = init; i < limit; i = ((i+1) << 16) >> 16) { // explicit shift instead of cast + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShift16BadBounds: short loop, with a CmpI, but the limit ranges are bad. + public static int testIRShift16BadBounds_gold = testIRShift16BadBounds(); + + @Run(test = "testIRShift16BadBounds") + private static void runIRShift16BadBounds() { + int val = testIRShift16BadBounds(); + if (val != testIRShift16BadBounds_gold) { throw new RuntimeException("wrong value: " + testIRShift16BadBounds_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShift16BadBounds() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100_000); // limit in [min_int..100_000] + int sum = 0; + // Now, the check is not good enough: + // -> init < limit <= 100_000 + // -> filtered_int_type return [min_int..99_999] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99_999], which is NOT in short range. + for (int i = init; i < limit; i = ((i+1) << 16) >> 16) { // explicit shift instead of cast + sum = opaqueSum(sum); // work to keep loop alive + // Also: the backedge range is not good because + // the exit check is not strong enough for short: + // i < limit <= 100_000 + } + return sum; + } + + // testIRShift8: 24-bit loop, and range in 24-bit range via CmpI before loop (for loop limit). + // Note: this shift value is strange, we probably wanted to implement byte truncation + // with shift=24, but instead we have 24-bit signed truncation. + // Note2: this pattern would have been supported by TruncatedIncrement::build, but it gets + // modified by LShiftINode::Ideal: + // RShiftI(AddI(LShiftI(Phi, 8), 256), 8) + // The same is explicitly excluded for shift 16, to preserve short/byte idioms. + public static int testIRShift8_gold = testIRShift8(); + + @Run(test = "testIRShift8") + private static void runIRShift8() { + int val = testIRShift8(); + if (val != testIRShift8_gold) { throw new RuntimeException("wrong value: " + testIRShift8_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShift8() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 100); // limit in [min_int..100] + int sum = 0; + // While there is no explicit CmpI before the loop, we + // actually have "i < limit" in the for loop check, which + // is also checked before entering the loop. + // So also here, we have: + // -> init < limit <= 100 + // -> filtered_int_type return [min_int..99] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..99], which is in 24-bit (and byte) range. + for (int i = init; i < limit; i = ((i+1) << 8) >> 8) { // explicit shift instead of cast + sum = opaqueSum(sum); // work to keep loop alive + } + return sum; + } + + // testIRShift8BadBounds: 24-bit loop, with a CmpI, but the limit ranges are bad. + // Note: same issues as for testIRShift8. + // Note2: the range argument seems a bit strange here, but it turns out that + // TruncatedIncrement::build maps shift=8 to BYTE, which just shows that + // the implementation confused the shift=24 with shift=8. + // Since we map to BYTE, 1_000 would be out of bounds, that's why this + // is still a bad bounds example. + public static int testIRShift8BadBounds_gold = testIRShift8BadBounds(); + + @Run(test = "testIRShift8BadBounds") + private static void runIRShift8BadBounds() { + int val = testIRShift8BadBounds(); + if (val != testIRShift8BadBounds_gold) { throw new RuntimeException("wrong value: " + testIRShift8BadBounds_gold + " vs " + val); } + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) + static int testIRShift8BadBounds() { + int init = Math.max(lo, 0); // init in [0..max_int] + int limit = Math.min(hi, 1_000); // limit in [min_int..1_000] + int sum = 0; + // Now, the check is not good enough: + // -> init < limit <= 1_000 + // -> filtered_int_type return [min_int..999] + // -> and intersected with its previous type [0..max_int] + // we get init in [0..999], which is NOT in byte range. + for (int i = init; i < limit; i = ((i+1) << 8) >> 8) { // explicit shift instead of cast + sum = opaqueSum(sum); // work to keep loop alive + // Also: the backedge range is not good because + // the exit check is not strong enough for byte: + // i < limit <= 1_000 + } + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java b/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java index 69f86a2bf1d..f557a491160 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java @@ -30,7 +30,7 @@ import compiler.lib.ir_framework.*; * @bug 8347499 * @summary Tests that redundant safepoints can be eliminated in loops. * @library /test/lib / - * @run main compiler.loopopts.TestRedundantSafepointElimination + * @run driver ${test.main.class} */ public class TestRedundantSafepointElimination { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java new file mode 100644 index 00000000000..ed279347762 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java @@ -0,0 +1,115 @@ +/* + * 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 compiler.loopopts; + +/* + * @test + * @bug 8386591 + * @summary Test case for TruncatedIncrement::build / + * CountedLoopConverter::has_truncation_wrap where we got wrong + * results, because we confused "& 0x7fff" as range [0..65535] + * instead of [0..32767]. + * @library /test/lib / + * @run main/othervm -Xcomp + * -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestTruncationWrapBadCharWrap { + interface TestMethod { + int call(); + } + + public static void main(String[] args) { + int failures = 0; + + failures += run("test1", () -> test1(), 1402); + failures += run("test2", () -> test2(), 2037); + failures += run("test3", () -> test3(), 171761184); + + if (failures > 0) { + throw new RuntimeException("failures: " + failures); + } + } + + static int run(String name, TestMethod t, int expected) { + for (int i = 0; i < 10_000; i++) { + int result = t.call(); + if (result != expected) { + System.out.println(name + " wrong result: " + result + " vs " + expected); + return 1; + } + } + return 0; + } + + + static int test1() { + int sum = 0; + // The entry value is outside the range [0..32767], but inside [0..65535]. + int i = (char)38405; + while (32 < i) { + sum++; + // Ignoring truncation would require values to be in range [0..32767]. + // But unfortunately, we classified this as CHAR, and checked for [0..65535]. + i = (i - 4) & 0x7fff; + } + return sum; + } + + static int test2() { + int sum = 0; + // We have 32767 - 32758 = 9 < 48, so the limit is too close to the wrap + // limit, and wrap is possible. But since 0x7fff got mapped to CHAR, + // we accidentally checked 65535 - 32758 < 48, and conclude wrap is not + // possible. + for (int i = 519; i < 32758; i = (i + 48) & 0x7fff) { + sum++; + } + return sum; + } + + static int opaqueCounter; + + static boolean opaqueCheck() { + return opaqueCounter++ > 10448; + } + + static int test3() { + opaqueCounter = 0; + int sum = 0; + int i; + // Similar as with test2: + // We should check 32767 - 32766 = 1 < 50, so wrap possible. But we + // wrongly classified it as CHAR and checked 65535 - 32766 < 50, and + // concluded there is no wrap. + for (i = 2046; i <= 32766; i = (i + 50) & 0x7fff) { + sum += i + 1; + if (opaqueCheck()) { break; } + } + return sum + i; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapEmptyType.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapEmptyType.java new file mode 100644 index 00000000000..5d6d49796f2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapEmptyType.java @@ -0,0 +1,65 @@ +/* + * 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 compiler.loopopts; + +/* + * @test + * @bug 8386482 + * @summary Test case for CountedLoopConverter::filtered_type_from_dominators/ + * CountedLoopConverter::has_truncation_wrap where the type becomes + * empty / top. This used to trigger the assert: + * assert(_base == Int) failed: Not an Int + * @library /test/lib / + * @run main/othervm -Xcomp + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestTruncationWrapEmptyType { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(1); + } + } + + static int test(int init) { + if (init < 1) { + return -1; + } + // if implies: init in [1..max_int] + + int i = init; + while (i < 0) { + // while implies: init in [min_int .. 0] + // That contradicts the "if" above: empty intersection + // + // See CountedLoopConverter::filtered_type_from_dominators: + // rtn_t = rtn_t->join(if_t)->is_int() + // -> top type in join, fails "is_int". + i = (short)(i + 1); + } + return 0; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java new file mode 100644 index 00000000000..915808d07cb --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java @@ -0,0 +1,535 @@ +/* + * 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 8386597 8385855 8386482 8386591 8386830 + * @summary Fuzz patterns for CountedLoopConverter::has_truncation_wrap + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../lib/ir_framework/TestFramework.java + * @compile ../lib/generators/Generators.java + * @run driver ${test.main.class} + */ + +package compiler.loopopts; + +import java.util.List; +import java.util.ArrayList; +import java.util.Random; +import java.util.Set; + +import jdk.test.lib.Utils; + +import compiler.lib.compile_framework.*; +import compiler.lib.generators.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; + +import compiler.lib.template_framework.library.TestFrameworkClass; + +/** + * For more basic examples, see: + * - TestHasTruncationWrap.java + * - TestTruncationWrapEmptyType.java + * - TestTruncationWrapPhiTypeUnion.java + * - TestTruncationWrapBadCharWrap.java + * + * So far, this test does not have IR verification, only result verification. + * + * This test generates a wide range of patterns, and will require a lot of + * runs to find a specific code shape. + * + * Features: + * - Truncation patterns, see TRUNCATIONS and randomIVMutation. + * - Stride: positive, negative, small and large, see ivMutationWithRandomStride. + * - Reference (not compiled) vs test (compiled), and result verification. + * - Loop Shapes: for, while, do-while, see LOOP_SHAPES. + * - Exit checks: random Comparison, see Comparator and Comparison (signed and unsigned). + * - For endless loops / loops that would take too long: early exit via opaqueCheck, + * Note: it is verified that this does not hinder optimization, see: + * TestHasTruncationWrap.java -> testIRShort7. + * - Interesting loop bounds: init/limit + * - constant + * - variable, sampled (see getInputTemplate), and modified (no-op, truncate, clamp). + * - Extra check dominating the loop: compare against constant of limit. + * Note: has_truncation_wrap can use such checks to constrain the entry type. + * Note2: We've had bugs around this, confusing CmpI/CmpU, see JDK-8385855. + */ +public class TestTruncationWrapFuzzer { + private static final Random RANDOM = Utils.getRandomInstance(); + private static final RestrictableGenerator INT_GEN = Generators.G.ints(); + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + long t0 = System.nanoTime(); + // Add a java source file. + comp.addJavaSourceCode("compiler.loopopts.templated.Generated", generate(comp)); + + long t1 = System.nanoTime(); + // Compile the source file. + comp.compile(); + + long t2 = System.nanoTime(); + + // Run the tests without any additional VM flags. + comp.invoke("compiler.loopopts.templated.Generated", "main", new Object[] {new String[] {}}); + long t3 = System.nanoTime(); + + System.out.println("Code Generation: " + (t1-t0) * 1e-9f); + System.out.println("Code Compilation: " + (t2-t1) * 1e-9f); + System.out.println("Running Tests: " + (t3-t2) * 1e-9f); + } + + public static String generate(CompileFramework comp) { + // Create a list to collect all tests. + List testTemplateTokens = new ArrayList<>(); + + // Some utilities, to help us get an additional exit, in case the + // generated loops spin too long, or are infinite loops. + Template.ZeroArgs utilsTemplate = Template.make(() -> scope( + """ + private static final Random RANDOM = Utils.getRandomInstance(); + + public static int opaqueCounter; + public static int opaqueCounterMax; + + @DontInline + public static void opaqueReset() { + opaqueCounter = 0; + } + + @DontInline + public static boolean opaqueCheck() { + return (opaqueCounter++) > opaqueCounterMax; + } + + @DontInline + public static int opaqueSum(int i, int j) { + return i + j + 1; + } + """ + )); + testTemplateTokens.add(utilsTemplate.asToken()); + + for (int i = 0; i < 100; i++) { + testTemplateTokens.add(generateTest(/* no warmup, like -Xcomp */ 0)); + } + for (int i = 0; i < 5; i++) { + testTemplateTokens.add(generateTest(/* with warmup, slower */ 100)); + } + + // Create the test class, which runs all testTemplateTokens. + return TestFrameworkClass.render( + // package and class name. + "compiler.loopopts.templated", "Generated", + // List of imports. + Set.of("compiler.lib.generators.*", + "java.util.Random", + "jdk.test.lib.Utils"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + testTemplateTokens); + } + + // This is copied from TestFoldComparesFuzzer.java, and we should + // refactor this out into the template framework library, in a + // future RFE. + enum Comparator { + ULT(" < 0", false), + ULE(" <= 0", false), + UGT(" > 0", false), + UGE(" >= 0", false), + UEQ(" == 0", false), + UNE(" != 0", false), + LT(" < ", true), + LE(" <= ", true), + GT(" > ", true), + GE(" >= ", true), + EQ(" == ", true), + NE(" != ", true); + + private final String token; + private final boolean signed; + + Comparator(String token, boolean signed) { + this.token = token; + this.signed = signed; + } + + public String getToken() { + return token; + } + + public boolean isSigned() { + return signed; + } + + public Comparator negate() { + return switch(this) { + case ULT -> UGE; + case ULE -> UGT; + case UGT -> ULE; + case UGE -> ULT; + case UEQ -> UNE; + case UNE -> UEQ; + case LT -> GE; + case LE -> GT; + case GT -> LE; + case GE -> LT; + case EQ -> NE; + case NE -> EQ; + }; + } + + public Comparator flip() { + return switch(this) { + case ULT -> UGT; + case ULE -> UGE; + case UGT -> ULT; + case UGE -> ULE; + case UEQ -> UEQ; + case UNE -> UNE; + case LT -> GT; + case LE -> GE; + case GT -> LT; + case GE -> LE; + case EQ -> EQ; + case NE -> NE; + }; + } + + static Comparator random() { + return values()[RANDOM.nextInt(values().length)]; + } + } + + record Comparison(String lhs, Comparator cmp, String rhs, boolean negated) { + public Comparison(String lhs, Comparator cmp, String rhs) { + this(lhs, cmp, rhs, false); + } + + public String toString() { + return cmp.isSigned() + ? ((negated ? "!" : "") + "(" + lhs + " "+ cmp.getToken() + " " + rhs + ")") + : ((negated ? "!" : "") + "(Integer.compareUnsigned(" + lhs + ", " + rhs + ")" + cmp.getToken() + ")"); + } + + // Keep the same semantics of the test, but change its form. + Comparison permuteRandom() { + return flipRandom().complementRandom(); + } + + Comparison flipRandom() { + return RANDOM.nextBoolean() ? this : new Comparison(rhs, cmp.flip(), lhs); + } + + Comparison complementRandom() { + return RANDOM.nextBoolean() ? this : new Comparison(lhs, cmp.negate(), rhs, true); + } + } + + interface TestMethodGenerator { + Template.OneArg getTestTemplate(); + + default Template.ZeroArgs getInputTemplate() { + return Template.make(() -> scope( + switch (RANDOM.nextInt(5)) { + case 0 -> """ + RestrictableGenerator gen = Generators.G.ints(); + int init = gen.next(); + int limit = gen.next(); + """; + case 1 -> """ + int init = (byte)RANDOM.nextInt(); + int limit = (byte)RANDOM.nextInt(); + """; + case 2 -> """ + int init = (short)RANDOM.nextInt(); + int limit = (short)RANDOM.nextInt(); + """; + case 3 -> """ + int init = (char)RANDOM.nextInt(); + int limit = (char)RANDOM.nextInt(); + """; + case 4 -> """ + int e0 = RANDOM.nextInt(32); + int e1 = RANDOM.nextInt(32); + int r0 = RANDOM.nextInt(32); + int r1 = RANDOM.nextInt(32); + int init = (1 << e0) + r0; + int limit = (1 << e1) + r1; + """; + default -> throw new RuntimeException("not expected"); + } + )); + }; + } + + private static record Truncation(String s0, String s1) { + public String ivMutationWithRandomStride() { + int stride = switch(RANDOM.nextInt(3)) { + case 0 -> INT_GEN.next(); + case 1 -> RANDOM.nextInt(9) - 4; + case 2 -> RANDOM.nextInt(129) - 64; + default -> throw new RuntimeException("not expected"); + }; + + return "i = " + s0 + "i + " + stride + s1; + } + + public String truncate(String val) { + return val + " = " + s0 + val + s1; + } + } + + // Different patterns relevant for triggering truncation/wrap. + private static final Truncation[] TRUNCATIONS = new Truncation[] { + new Truncation("", ""), + new Truncation("(byte)(", ")"), + new Truncation("(short)(", ")"), + new Truncation("(char)(", ")"), + new Truncation("((", ") << 8) >> 8"), + new Truncation("((", ") << 16) >> 16"), + new Truncation("((", ") << 24) >> 24"), + new Truncation("((", ") & 0x7f)"), + new Truncation("((", ") & 0xff)"), + new Truncation("((", ") & 0x7fff)"), + new Truncation("((", ") & 0xffff)") + }; + + private static Truncation randomTruncation() { + return TRUNCATIONS[RANDOM.nextInt(TRUNCATIONS.length)]; + } + + private static String randomIVMutation() { + return randomTruncation().ivMutationWithRandomStride(); + } + + private static String randomTruncation(String val) { + return randomTruncation().truncate(val); + } + + private static final String[] LOOP_SHAPES = new String[] { + """ + // Loop Shape: For + int i; + for (i = init; #exitCheck; #ivMutation) { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + } + """, + """ + // Loop Shape: While: + int i = init; + while (#exitCheck) { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + #ivMutation; + } + """, + """ + // Loop Shape: Do-While: + int i = init; + do { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + #ivMutation; + } while (#exitCheck); + """, + """ + // Loop Shape: Do-While + pre-loop check. + int i = init; + if (!(#exitCheck)) { return sum; } + do { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + #ivMutation; + } while (#exitCheck); + """ + }; + + private static String randomLoopShape() { + return LOOP_SHAPES[RANDOM.nextInt(LOOP_SHAPES.length)]; + } + + // Loop init/limit are constants. + static class TestMethodGeneratorConst implements TestMethodGenerator { + private final int init = INT_GEN.next(); + private final int limit = INT_GEN.next(); + + private final String ivMutation = randomIVMutation(); + private final String loopShape = randomLoopShape(); + private final String addValue = RANDOM.nextBoolean() ? "0" : "i"; + + private final Comparison exitCheck = new Comparison("i", Comparator.random(), "limit").permuteRandom(); + + private final Template.OneArg testTemplate = Template.make("methodName", (String methodName) -> scope( + let("init", init), + let("limit", limit), + let("ivMutation", ivMutation), + let("exitCheck", exitCheck), + let("addValue", addValue), + """ + static int #methodName(int unused0, int unused1) { + opaqueReset(); + int init = #init; + int limit = #limit; + int sum = 0; + """, + loopShape, + """ + return sum + #addValue; + } + """ + )); + + public Template.OneArg getTestTemplate() { return testTemplate; } + } + + // Clamp randomly, but not always on both sides. + private static String randomClamping(String value) { + String clamp = value; + if (RANDOM.nextBoolean()) { + clamp = "Math.max(" + clamp + ", " + INT_GEN.next() + ")"; + } + if (RANDOM.nextBoolean()) { + clamp = "Math.min(" + clamp + ", " + INT_GEN.next() + ")"; + } + return value + " = " + clamp; + } + + // We want to be able to modify the incoming init/limit. + // - nothing + // - truncate + // - clamp with min/max, maybe even only one-sided + private static String randomModifyValue(String value) { + return switch(RANDOM.nextInt(3)) { + case 0 -> "// Don't modify " + value + "\n"; + case 1 -> randomTruncation(value) + ";\n"; + case 2 -> randomClamping(value) + ";\n"; + default -> throw new RuntimeException("not expected"); + }; + } + + private static String randomExtraCheck() { + // We can constrain the init value with limit or a constant. + String other = RANDOM.nextBoolean() ? "limit" : INT_GEN.next().toString(); + Comparison check = new Comparison("init", Comparator.random(), other).permuteRandom(); + return RANDOM.nextBoolean() + ? "// No extra check.\n" + : "if (" + check + ") { return -1; }\n"; + } + + // Loop init/limit are variables. + static class TestMethodGeneratorVars implements TestMethodGenerator { + private final String ivMutation = randomIVMutation(); + private final String loopShape = randomLoopShape(); + private final String addValue = RANDOM.nextBoolean() ? "0" : "i"; + private final String modifyInit = randomModifyValue("init"); + private final String modifyLimit = randomModifyValue("limit"); + private final String extraCheck = randomExtraCheck(); + + private final Comparison exitCheck = new Comparison("i", Comparator.random(), "limit").permuteRandom(); + + private final Template.OneArg testTemplate = Template.make("methodName", (String methodName) -> scope( + let("ivMutation", ivMutation), + let("exitCheck", exitCheck), + let("addValue", addValue), + """ + static int #methodName(int init, int limit) { + opaqueReset(); + int sum = 0; + """, + modifyInit, // modify type of init + modifyLimit, // modify type of limit + extraCheck, // extra CmpI/CmpU dominating the loop, might constrain entry value. + loopShape, + """ + return sum + #addValue; + } + """ + )); + + public Template.OneArg getTestTemplate() { return testTemplate; } + } + public static TemplateToken generateTest(int warmup) { + TestMethodGenerator tg = switch(RANDOM.nextInt(2)) { + case 0 -> new TestMethodGeneratorConst(); + case 1 -> new TestMethodGeneratorVars(); + default -> throw new RuntimeException("not expected"); + }; + Template.ZeroArgs testInputTemplate = tg.getInputTemplate(); + Template.OneArg testMethodTemplate = tg.getTestTemplate(); + + var testTemplate = Template.make(() -> scope( + let("warmup", warmup), + """ + // --- $test start --- + @Run(test = "$test") + @Warmup(#warmup) + public static void $run(RunInfo info) { + int reps = info.isWarmUp() ? 1 : 100; + for (int i = 0; i < reps; i++) { + // Generate random values for init and limit. + """, + testInputTemplate.asToken(), + """ + + // Limit how long we can spin in the loop: + opaqueCounterMax = 10_000 + RANDOM.nextInt(1000); + + // Run test and compare with interpreter results. + var result = $test(init, limit); + var expected = $reference(init, limit); + if (result != expected) { + throw new RuntimeException("wrong result: " + result + " vs " + expected + + "\\ninit: " + init + + "\\nlimit: " + limit + + "\\nopaqueCounterMax: " + opaqueCounterMax); + } + } + } + + @Test + """, + testMethodTemplate.asToken($("test")), + """ + + @DontCompile + """, + testMethodTemplate.asToken($("reference")), + """ + // --- $test end --- + """ + )); + return testTemplate.asToken(); + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java new file mode 100644 index 00000000000..a7873359b45 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java @@ -0,0 +1,221 @@ +/* + * 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 compiler.loopopts; + +/* + * @test + * @bug 8386830 + * @summary Test for CountedLoopConverter::filtered_type, where we wrongly + * ignored a nullptr type, and returned a type that was too narrow, + * which led us to wrongly ignore wrapping in + * CountedLoopConverter::has_truncation_wrap + * @library /test/lib / + * @run main/othervm -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + * @run main ${test.main.class} + */ +public class TestTruncationWrapPhiTypeUnion { + + interface TestMethod { + int call(); + } + + public static void main(String[] args) { + int failures = 0; + + failures += run("test1", () -> test1(-1), 11111); + failures += run("test2", () -> test2(-1), 11111); + failures += run("test3", () -> test3(-100_000), -87065049); + failures += run("test4", () -> test4(32770), 10330); + failures += run("test5", () -> test5(-63), 10340); + + if (failures > 0) { + throw new RuntimeException("failures: " + failures); + } + } + + static int run(String name, TestMethod t, int expected) { + for (int i = 0; i < 20; i++) { + int result = t.call(); + if (result != expected) { + System.out.println(name + " wrong result: " + result + " vs " + expected); + return 1; + } + } + return 0; + } + + static int test1(int limit) { + int x = 0; + int sum = 0; + + limit = (byte) limit; // type BYTE = [-128..127], at runtime: -1 + + int i = 510; + while (limit < i) { + // Exit check checks for positive values, but with + // entry 510 and unsigned truncation, that can never fail. + + sum++; + + // Secondary exit check, to make sure we exit eventually. + if (++x >= 11111) { + break; + } + + // Unsigned 15-bit truncation. + // We check for wrap/truncation/underflow: + // + // } else if (stride_con < 0) { + // if (truncation.trunc_type()->lo_as_long() - phi_ft->lo_as_long() > stride_con || + // truncation.trunc_type()->hi_as_long() < phi_ft->hi_as_long()) { + // return true; // truncation may occur + // } + // } + // + // The lo of truncation is 0, and also the phi type should have a lo of 0, + // but it was wrongly determined to be 510. + // So, whereas "0 - 0 > -10" would have given us the required "true", + // we now checked "0 - 510 > -10", which was wrongly "false". + // + // The reason is that the phi_ft has been wrongly determined to be 510, + // so only considering the entry value. This is determined inside filtered_type: + // - entry: filtered_type_from_dominators discovers entry value 510. + // - backedge: filtered_type_from_dominators discovers no dominating-if, returns nullptr. + // But filtered_type skips nullptr results, as in "no extra filter". But + // we should be accumulating the entry and backedge type here! + // + // The only if on the backedge-path would have been the exit + // check: limit < i. But filtered_int_type finds nothing, returns nullptr. + i = (i - 10) & 0x7fff; + } + + return sum; + } + + // Note: this case was first discovered during JDK-8386591, which enabled 0xffff masking. + // Without allowing 0xffff masking, this did not fail. But it was the way I + // first discovered the bug, and so I wanted to add this as a test anyway. + static int test2(int limit) { + int x = 0; + int sum = 0; + + limit = (short) limit; // type SHORT = [-32768..32767], at runtime: -1 + + int i = 510; + while (limit < i) { + // Exit check checks for positive values, but with + // entry 510 and unsigned truncation, that can never fial. + + sum++; + + // Secondary exit check, to make sure we exit eventually. + if (++x >= 11111) { + break; + } + + // CHAR truncation: 0..0xffff = 0..65535 + // + // i iterates: 510, 500, ... 10, 0 + // And then, it shshould ould underflows: (0 - 10) & 0xffff = 65526 + // + // But in CountedLoopConverter::has_truncation_wrap, we wrongly + // decide there cannot be overflow. + // truncation: [0..65535] + // stride_con: -10 + // + // Accordingly, phi_ft should be in [0..65535], and so when we check + // for underflow, we check: + // + // } else if (stride_con < 0) { + // if (truncation.trunc_type()->lo_as_long() - phi_ft->lo_as_long() > stride_con || + // truncation.trunc_type()->hi_as_long() < phi_ft->hi_as_long()) { + // return true; // truncation may occur + // } + // } + // + // So we should check: 0 - 0 > -10, and we would see that truncation could occur. + // But instead, we checked 0 - 510 > -10, which wronly lead to "no truncation". + // + // The reason is that the phi_ft has been wrongly determined to be 510, + // so only considering the entry value. This is determined inside filtered_type: + // - entry: filtered_type_from_dominators discovers entry value 510. + // - backedge: filtered_type_from_dominators discovers no dominating-if, returns nullptr. + // But filtered_type skips nullptr results, as in "no extra filter". But + // we should be accumulating the entry and backedge type here! + // + // The only if on the backedge-path would have been the exit + // check: limit < i. But filtered_int_type finds nothing, returns nullptr. + i = (i - 10) & 0xffff; + } + + return sum; + } + + // Another fuzzer find, this one with short truncation. + static int test3(int limit) { + int x = 0; + int sum = 0; + + // Range: [min_int..8192], at runtime: -100_000 + limit = Math.min(limit, 8192); + int i; + for (i = 128; limit <= i; i = (short)(i - 16384)) { + sum = sum + i + 1; + if (x++ > 10789) { + break; + } + } + return sum + i; + } + + // Another fuzzer find, this one with short truncation. + static int test4(int limit) { + int sum = 0; + int x = 0; + limit = (short) limit; + for (int i = -1025; limit <= i; i = (short) (i + -7)) { + sum = sum + 1; + if (x++ > 10328) { + break; + } + } + return sum; + } + + // Another fuzzer find, again with 0x7fff mask. + static int test5(int limit) { + int x = 0; + int sum = 0; + limit = (byte)limit; + for (int i = 8192; limit < i; i = ((i + -8) & 0x7fff)) { + sum++; + if (x++ > 10338) { + break; + } + } + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitPPC64.java b/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitPPC64.java new file mode 100644 index 00000000000..a4340d230e7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitPPC64.java @@ -0,0 +1,187 @@ +/* + * 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. + */ + +/** + * @test TestOnSpinWaitPPC64 + * @summary Checks that java.lang.Thread.onSpinWait is intrinsified on PPC64 + * and emits the SMT priority-low / priority-medium nop pair. + * @library /test/lib + * + * @requires vm.flagless + * @requires os.arch=="ppc64" | os.arch=="ppc64le" + * @requires vm.debug + * + * @run driver compiler.onSpinWait.TestOnSpinWaitPPC64 c2 + * @run driver compiler.onSpinWait.TestOnSpinWaitPPC64 c1 + */ + +package compiler.onSpinWait; + +import java.util.ArrayList; +import java.util.Iterator; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestOnSpinWaitPPC64 { + + public static void main(String[] args) throws Exception { + String compiler = args[0]; + ArrayList command = new ArrayList(); + command.add("-XX:+IgnoreUnrecognizedVMOptions"); + command.add("-showversion"); + command.add("-XX:-BackgroundCompilation"); + command.add("-XX:+UnlockDiagnosticVMOptions"); + command.add("-XX:+PrintCompilation"); + command.add("-XX:+PrintInlining"); + if (compiler.equals("c2")) { + command.add("-XX:-TieredCompilation"); + } else if (compiler.equals("c1")) { + command.add("-XX:+TieredCompilation"); + command.add("-XX:TieredStopAtLevel=1"); + } else { + throw new RuntimeException("Unknown compiler: " + compiler); + } + command.add("-Xbatch"); + command.add("-XX:CompileCommand=compileonly," + Launcher.class.getName() + "::test"); + command.add("-XX:CompileCommand=print," + Launcher.class.getName() + "::test"); + command.add(Launcher.class.getName()); + + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(command); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + + analyzer.shouldHaveExitValue(0); + + System.out.println(analyzer.getOutput()); + + checkOutput(analyzer); + } + + // Hex encoding of the SMT priority-hint instructions emitted by the + // PPC `Thread.onSpinWait()` intrinsic. + // X-forms used: + // or 1,1,1 -> 0x7C21_0B78 (smt_prio_low) + // or 2,2,2 -> 0x7C42_1378 (smt_prio_medium) + private static String getSpinWaitInstructionHexLE(String name) { + if ("smt_prio_low".equals(name)) + return "780b217c"; + if ("smt_prio_medium".equals(name)) + return "7813427c"; + throw new RuntimeException("Unknown spin wait instruction: " + name); + } + + private static String getSpinWaitInstructionHexBE(String name) { + if ("smt_prio_low".equals(name)) + return "7c210b78"; + if ("smt_prio_medium".equals(name)) + return "7c421378"; + throw new RuntimeException("Unknown spin wait instruction: " + name); + } + + private static boolean lineContainsInstruction(String compact, String name) { + return compact.contains(getSpinWaitInstructionHexLE(name)) || + compact.contains(getSpinWaitInstructionHexBE(name)); + } + + // The expected output for the spin wait body if the hsdis library is available: + // + // ;; spin_wait { + // 0x...: or r1,r1,r1 + // 0x...: or r2,r2,r2 + // ;; } + // + // When hsdis is absent the disassembler dumps raw bytes which have to matched. + private static void checkOutput(OutputAnalyzer output) { + Iterator iter = output.asLines().listIterator(); + + // 1. Check whether printed instructions are disassembled. + boolean isDisassembled = false; + while (iter.hasNext()) { + String line = iter.next(); + if (line.contains("[Disassembly]")) { + isDisassembled = true; + break; + } + if (line.contains("[MachCode]")) { + break; + } + } + + // 2. Look for the spin_wait block comment. + boolean foundHead = false; + while (iter.hasNext()) { + String line = iter.next().trim(); + if (line.contains(";; spin_wait {")) { + foundHead = true; + break; + } + } + if (!foundHead) { + throw new RuntimeException("spin_wait block comment not found"); + } + + // 3. Expect prio low and prio med instructions inside the block. + boolean sawLow = false, sawMed = false; + while (iter.hasNext()) { + String line = iter.next().trim(); + if (line.startsWith(";; }")) { + break; + } + if (isDisassembled) { + // hsdis output: look for `or r1,r1,r1` and `or r2,r2,r2`. + // Also the mnemonic mr rx,rx needs to be matched. + String norm = line.replaceAll("\\s+", " "); + if (norm.contains("or r1,r1,r1") || norm.contains("mr r1,r1")) + sawLow = true; + if (norm.contains("or r2,r2,r2") || norm.contains("mr r2,r2")) + sawMed = true; + } else { + // without hsdis output: raw 4-byte instruction words. + String compact = line.replaceAll("\\s", "").toLowerCase(); + if (lineContainsInstruction(compact, "smt_prio_low")) + sawLow = true; + if (lineContainsInstruction(compact, "smt_prio_medium")) + sawMed = true; + } + } + + if (!sawLow) { + throw new RuntimeException("Did not find smt_prio_low (or r1,r1,r1) inside spin_wait block"); + } + if (!sawMed) { + throw new RuntimeException("Did not find smt_prio_medium (or r2,r2,r2) inside spin_wait block"); + } + } + + static class Launcher { + public static void main(final String[] args) throws Exception { + for (int i = 0; i < 20_000; i++) { + test(); + } + } + + static void test() { + java.lang.Thread.onSpinWait(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java b/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java new file mode 100644 index 00000000000..19214738dd6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java @@ -0,0 +1,84 @@ +/* + * 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 8387197 + * @summary Verify that improving klass_ptr_type in GraphKit::gen_instanceof() allows + * eliminating SubTypeCheckNode when the receiver implements an interface + * unrelated to the checked class. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.parsing; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +public class TestInstanceOfImprovedKlassPtrType { + static abstract class B {} + static final class C extends B {} + + interface I {} + static class D implements I {} + static class E implements I {} + + public static void main(String[] args) { + TestFramework.run(); + } + + @DontInline + int testHelper2(Object o) { + return 1; + } + + @Test + @IR(counts = {IRNode.SUBTYPE_CHECK, "1"}, + phase = CompilePhase.AFTER_PARSING) + int test1(Object o) { + Object o1 = (I) o; + if (o1 instanceof B) { + return testHelper2(o1); + } else { + return 2; + } + } + + @Run(test = "test1") + @Warmup(0) + void runTest() { + int sum = 0; + Object[] arr = new Object[] {new C(), new D(), new E()}; + for (int i = 0; i < 3; i++){ + Object o = arr[i]; + if (o instanceof I) { + sum += test1(o); + } else { + sum += 3; + } + } + Asserts.assertEquals(sum, 7); + return; + } +} diff --git a/test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java b/test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java new file mode 100644 index 00000000000..879234a62cf --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java @@ -0,0 +1,196 @@ +/* + * 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 compiler.parsing; + +import java.io.IOException; +import java.util.Objects; +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8387328 + * @summary A Phi having a narrower Type than its inputs may result in incorrect scheduling + * @library /test/lib + * @requires vm.compiler2.enabled + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * ${test.main.class} + */ +public class TestNarrowPhi { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static volatile Throwable failure; + + private static abstract class P { + int u; + + private static P allocate() { + return new C(); + } + } + + private static class C extends P { + int v; + } + + public static void main(String[] args) throws IOException, InterruptedException, NoSuchMethodException { + if (args.length == 0) { + spawnTestProcesses(); + } else { + int idx = Integer.parseInt(args[0]); + runTest(idx); + } + } + + private static void spawnTestProcesses() throws IOException, InterruptedException { + String testClassName = TestNarrowPhi.class.getName(); + // Since we cannot reliably coordinate the compiler thread and the thread that load the + // child class, randomly delaying one of them + for (int i = 0; i <= 10; i++) { + var builder = ProcessTools.createTestJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-Xbatch", + "-XX:-TieredCompilation", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:CompileOnly=" + testClassName + "::test*", + "-XX:CompileCommand=inline," + testClassName + "::inline*", + "-XX:CompileCommand=dontinline," + testClassName + "::nonInline", + "-XX:CompileCommand=delayinline," + testClassName + "::inlineTestHelper", + testClassName, + Integer.toString(i)); + builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + builder.redirectError(ProcessBuilder.Redirect.INHERIT); + var process = builder.start(); + process.waitFor(); + Asserts.assertEQ(0, process.exitValue()); + } + } + + private static void runTest(int idx) throws InterruptedException, NoSuchMethodException { + var testMethod = TestNarrowPhi.class.getDeclaredMethod("testMethod", boolean.class, P.class, P.class, P.class); + var _ = Objects.class; + Thread loader = new Thread(() -> { + try { + if (idx < 5) { + Thread.sleep((5 - idx) * 10L); + } + var _ = C.class; + } catch (Exception e) { + failure = e; + } + }); + loader.start(); + + if (idx > 5) { + Thread.sleep((idx - 5) * 10L); + } + if (!WHITE_BOX.enqueueMethodForCompilation(testMethod, 4)) { + throw new RuntimeException("Could not enqueue the test method for C2 compilation"); + } + while (WHITE_BOX.isMethodQueuedForCompilation(testMethod)) { + Thread.yield(); + } + P p = P.allocate(); + Asserts.assertEQ(0, testMethod(true, p, p, p)); + loader.join(); + if (failure != null) { + throw new RuntimeException(failure); + } + } + + private static int testMethod(boolean b, P p1, P p2, P p3) { + // Arbitrarily delay the parser between generating the Type for P1 and for the loop Phi + // below + inline0(); + // This method is late-inlined, which increases the chance that C has been loaded then + return inlineTestHelper(b, p1, p2, p3); + } + + private static int inlineTestHelper(boolean b, P p1, P p2, P p3) { + // Random access that can be used as an implicit null-check, so that the load below can + // float freely + p1.u = 0; + P p = p1; + for (int i = 0; i < 1; i++) { + if (i % 2 != 0) { + p = p2; + } + } + + C cp = (C) Objects.requireNonNull(p); + C cp3 = (C) Objects.requireNonNull(p3); + int res = cp.v; + cp3.v = 1; + if (b) { + cp3.v = 2; + return res; + } else { + return nonInline(); + } + } + + private static int nonInline() { + return 0; + } + + private static void inline0() { + inline1(); + inline1(); + inline1(); + inline1(); + } + + private static void inline1() { + inline2(); + inline2(); + inline2(); + inline2(); + } + + private static void inline2() { + inline3(); + inline3(); + inline3(); + inline3(); + } + + private static void inline3() { + inline4(); + inline4(); + inline4(); + inline4(); + } + + private static void inline4() { + inline5(); + inline5(); + inline5(); + inline5(); + } + + private static void inline5() {} +} diff --git a/test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java b/test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java new file mode 100644 index 00000000000..d6bd097eec0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java @@ -0,0 +1,240 @@ +/* + * 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 compiler.parsing; + +import jdk.internal.misc.Unsafe; +import jdk.test.whitebox.WhiteBox; + +/* + * @test + * @bug 8386503 + * @summary Test load folding from a field store with a less precise type + * @library /test/lib + * @requires vm.compiler2.enabled + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:-TieredCompilation + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:CompileOnly=${test.main.class}::test* + * -XX:CompileCommand=inline,${test.main.class}::inline* + * ${test.main.class} + */ +public class TestTypeUnsafeFieldStore { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static volatile Throwable failure; + + public static void main(String[] args) throws Exception { + for (int i = 0; i <= 10; i++) { + testConcurrentClassLoading(i); + } + Holder h = new Holder(); + Integer obj = 0; + for (int i = 0; i < 20000; i++) { + testUnsafeAccess(h, obj); + } + } + + // It's hard to coordinate the compiler thread with the thread that load the child class, so we + // randomly delay one of the threads + private static void testConcurrentClassLoading(int idx) throws Exception { + var parentClass = Class.forName("compiler.parsing.TestTypeUnsafeFieldStore$P" + idx); + var testMethod = TestTypeUnsafeFieldStore.class.getDeclaredMethod("testMethod" + idx, parentClass); + Thread compiler = new Thread(() -> { + try { + if (idx < 5) { + Thread.sleep((5 - idx) * 10L); + } + WHITE_BOX.markMethodProfiled(testMethod); + if (!WHITE_BOX.enqueueMethodForCompilation(testMethod, 4)) { + throw new RuntimeException("Could not enqueue the test method for C2 compilation"); + } + while (WHITE_BOX.isMethodQueuedForCompilation(testMethod)) { + Thread.yield(); + } + } catch (Throwable t) { + failure = t; + } + }); + compiler.start(); + if (idx > 5) { + Thread.sleep((idx - 5) * 10L); + } + Class.forName("compiler.parsing.TestTypeUnsafeFieldStore$C" + idx); + compiler.join(); + if (failure != null) { + throw new RuntimeException(failure); + } + } + + private static Integer testUnsafeAccess(Holder h, Object obj) { + UNSAFE.putReference(h, Holder.V_OFFSET, obj); + return h.v; + } + + private static class Holder { + private static final long V_OFFSET = UNSAFE.objectFieldOffset(Holder.class, "v"); + Integer v; + } + + // When the compiler parses the store, C has not been loaded, so obj is of type P. However, + // when the compiler parses the load, C has been loaded and is observed to be the unique + // concrete subclass of P, so the result of the load is of type C. Folding the load to obj will + // drop this information, thus is incorrect. + private static abstract class P0 {} + private static class C0 extends P0 {} + private static P0 staticField0; + private static P0 testMethod0(P0 obj) { + staticField0 = obj; + inline0(); + return staticField0; + } + + private static abstract class P1 {} + private static class C1 extends P1 {} + private static P1 staticField1; + private static P1 testMethod1(P1 obj) { + staticField1 = obj; + inline0(); + return staticField1; + } + + private static abstract class P2 {} + private static class C2 extends P2 {} + private static P2 staticField2; + private static P2 testMethod2(P2 obj) { + staticField2 = obj; + inline0(); + return staticField2; + } + + private static abstract class P3 {} + private static class C3 extends P3 {} + private static P3 staticField3; + private static P3 testMethod3(P3 obj) { + staticField3 = obj; + inline0(); + return staticField3; + } + + private static abstract class P4 {} + private static class C4 extends P4 {} + private static P4 staticField4; + private static P4 testMethod4(P4 obj) { + staticField4 = obj; + inline0(); + return staticField4; + } + + private static abstract class P5 {} + private static class C5 extends P5 {} + private static P5 staticField5; + private static P5 testMethod5(P5 obj) { + staticField5 = obj; + inline0(); + return staticField5; + } + + private static abstract class P6 {} + private static class C6 extends P6 {} + private static P6 staticField6; + private static P6 testMethod6(P6 obj) { + staticField6 = obj; + inline0(); + return staticField6; + } + + private static abstract class P7 {} + private static class C7 extends P7 {} + private static P7 staticField7; + private static P7 testMethod7(P7 obj) { + staticField7 = obj; + inline0(); + return staticField7; + } + + private static abstract class P8 {} + private static class C8 extends P8 {} + private static P8 staticField8; + private static P8 testMethod8(P8 obj) { + staticField8 = obj; + inline0(); + return staticField8; + } + + private static abstract class P9 {} + private static class C9 extends P9 {} + private static P9 staticField9; + private static P9 testMethod9(P9 obj) { + staticField9 = obj; + inline0(); + return staticField9; + } + + private static abstract class P10 {} + private static class C10 extends P10 {} + private static P10 staticField10; + private static P10 testMethod10(P10 obj) { + staticField10 = obj; + inline0(); + return staticField10; + } + + private static void inline0() { + inline1(); + inline1(); + inline1(); + inline1(); + } + + private static void inline1() { + inline2(); + inline2(); + inline2(); + inline2(); + } + + private static void inline2() { + inline3(); + inline3(); + inline3(); + inline3(); + } + + private static void inline3() { + inline4(); + inline4(); + inline4(); + inline4(); + } + + private static void inline4() { + inline5(); + inline5(); + inline5(); + inline5(); + } + + private static void inline5() {} +} diff --git a/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java b/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java index ee368b54bea..b633cfa46ca 100644 --- a/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java +++ b/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Loongson Technology Co. Ltd. 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 @@ -25,7 +26,8 @@ * @test * @bug 8230943 * @summary possible deadlock was detected when ran with -XX:+CIPrintCompileQueue - * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+CIPrintCompileQueue + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:+CIPrintCompileQueue * compiler.print.PrintCompileQueue * */ diff --git a/test/hotspot/jtreg/compiler/print/PrintInlining.java b/test/hotspot/jtreg/compiler/print/PrintInlining.java index 4b45a32949f..486231cc50f 100644 --- a/test/hotspot/jtreg/compiler/print/PrintInlining.java +++ b/test/hotspot/jtreg/compiler/print/PrintInlining.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, 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 @@ -25,12 +25,12 @@ * @test * @bug 8022585 8277055 * @summary VM crashes when ran with -XX:+PrintInlining - * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining - * compiler.print.PrintInlining - * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining - * compiler.print.PrintInlining - * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintIntrinsics + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput -XX:+PrintInlining * compiler.print.PrintInlining + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+PrintInlining compiler.print.PrintInlining + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+PrintIntrinsics compiler.print.PrintInlining */ package compiler.print; diff --git a/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java b/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java index 22ce12f9641..726f7820797 100644 --- a/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java +++ b/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.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 @@ -25,7 +25,8 @@ * @test * @bug 8258229 * @summary If a method is made not entrant while printing the assembly, hotspot crashes due to mismatched relocation information. - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+DeoptimizeALot + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:-TieredCompilation -XX:+DeoptimizeALot * -XX:CompileCommand=print,java/math/BitSieve.bit compiler.print.TestPrintAssemblyDeoptRace */ diff --git a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java index 529469e3a95..85d3e19504c 100644 --- a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java +++ b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Red Hat 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 @@ -25,7 +26,9 @@ * @test * @bug 8335843 * @summary C2 hits assert(_print_inlining_stream->size() > 0) failed: missing inlining msg - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining TestPrintInliningLateMHCall + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining + * TestPrintInliningLateMHCall */ import java.lang.invoke.MethodHandle; diff --git a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java index f73e30badcb..63a7215bf5b 100644 --- a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java +++ b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Red Hat 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 @@ -25,7 +26,9 @@ * @test * @bug 8327741 * @summary JVM crash in hotspot/share/opto/compile.cpp - failed: missing inlining msg - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining TestPrintInliningLateVirtualCall + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining + * TestPrintInliningLateVirtualCall */ public class TestPrintInliningLateVirtualCall { diff --git a/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java b/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java index 15f60ac3e77..cb5f67bc61c 100644 --- a/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java +++ b/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, 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 @@ -26,7 +26,8 @@ * @bug 8073154 * @run main/othervm -XX:TypeProfileLevel=020 * -XX:CompileCommand=compileonly,compiler.print.TestProfileReturnTypePrinting::testMethod - * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintLIR + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+PrintLIR * compiler.print.TestProfileReturnTypePrinting * @summary Verify that c1's LIR that contains ProfileType node could be dumped * without a crash disregard to an exact class knowledge. diff --git a/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java b/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java index 52a7aba1a7e..3d3b242b4ce 100644 --- a/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java +++ b/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Tencent. 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 @@ -26,7 +27,8 @@ * @bug 8293785 * @summary test for -XX:+TraceOptoParse * @requires vm.debug & vm.compiler2.enabled - * @run main/othervm -XX:+TraceOptoParse compiler.print.TestTraceOptoParse + * @run main/othervm -XX:-DisplayVMOutput -XX:+TraceOptoParse + * compiler.print.TestTraceOptoParse * */ diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java b/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java index bec3e442403..b0df68b209a 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.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 @@ -27,14 +27,14 @@ * @summary Test logic in IfNode::fold_compares, which folds 2 signed comparisons * into a single comparison. * @library /test/lib / - * @run main ${test.main.class} + * @run driver ${test.main.class} */ /* * @test id=Xcomp * @bug 8346420 * @library /test/lib / - * @run main ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + * @run driver ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* */ package compiler.rangechecks; diff --git a/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java b/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java index 7c84450c778..29508cf9092 100644 --- a/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java +++ b/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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,7 +26,9 @@ * @bug 8044538 * @summary assert hit while printing relocations for jump table entries * - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:CompileCommand=compileonly,java.lang.String*::* -XX:+PrintRelocations + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -Xcomp + * -XX:CompileCommand=compileonly,java.lang.String*::* -XX:+PrintRelocations * compiler.relocations.TestPrintRelocations */ /** diff --git a/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java b/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java index b9f9343dd39..8f967fae560 100644 --- a/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java +++ b/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java @@ -27,7 +27,7 @@ * @modules java.base/jdk.internal.lang * @library /test/lib / * @enablePreview - * @run main ${test.main.class} + * @run driver ${test.main.class} */ package compiler.stable; diff --git a/test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java b/test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java new file mode 100644 index 00000000000..d3ce317ed30 --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java @@ -0,0 +1,51 @@ +/* + * 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 8382536 + * @summary C2: sharpen_type_after_if: assert(val->find_edge(con) > 0) failed: mismatch + * + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,${test.main.class}::test ${test.main.class} + */ +package compiler.types; + +public class TestSubTypeCheckConstantCastII { + static class A {} + + static boolean isInstanceOfA(Object obj) { + return (obj instanceof A); + } + + static void test(boolean b, Object obj) { + if (b) { + return; + } + // b == false + if (b != isInstanceOfA(obj)) {} + } + + public static void main(String[] args) { + test(true, new A()); + } +} diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java index 2866a84ba46..7bbe82c7311 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java @@ -26,7 +26,9 @@ * @bug 8374862 * @summary Regression test for -XX:+Verbose -XX:+WizardMode -XX:+PrintDeoptimizationDetails crash * @requires vm.debug - * @run main/othervm -XX:+Verbose -XX:+WizardMode -XX:+PrintDeoptimizationDetails compiler.uncommontrap.TestDeoptDetailsLockRank + * @run main/othervm -XX:-DisplayVMOutput + * -XX:+Verbose -XX:+WizardMode -XX:+PrintDeoptimizationDetails + * compiler.uncommontrap.TestDeoptDetailsLockRank */ package compiler.uncommontrap; @@ -36,4 +38,4 @@ public class TestDeoptDetailsLockRank { public static void main(String[] args) { System.out.println("passed"); } -} \ No newline at end of file +} diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java index 7a4f15d6461..21a3c08b665 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java @@ -41,7 +41,8 @@ * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::main * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::m9_1 * -XX:+UnlockDiagnosticVMOptions - * -XX:+UseZGC -XX:+LogCompilation -XX:+PrintDeoptimizationDetails -XX:+TraceDeoptimization -XX:+Verbose + * -XX:-DisplayVMOutput -XX:+UseZGC -XX:+LogCompilation + * -XX:+PrintDeoptimizationDetails -XX:+TraceDeoptimization -XX:+Verbose * compiler.uncommontrap.TestDeoptOOM */ diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java index 51b30219aca..6fb22ec0759 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java @@ -28,7 +28,7 @@ * -XX:-TieredCompilation -Xcomp crash * @modules java.base/jdk.internal.misc * @requires vm.debug - * @run main/othervm -XX:+TraceDeoptimization -XX:-ProfileTraps + * @run main/othervm -XX:-DisplayVMOutput -XX:+TraceDeoptimization -XX:-ProfileTraps * -XX:-TieredCompilation -Xcomp -Xbatch * -XX:CompileCommand=compileonly,compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps::test * compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps @@ -55,4 +55,4 @@ public class TestPrintDiagnosticsWithoutProfileTraps { test(); System.out.println("passed"); } -} \ No newline at end of file +} diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java b/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java index 4cd10a1a63e..abac84cdf23 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, 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 @@ -27,7 +27,8 @@ * @summary -XX:+TraceDeoptimization tries to print realloc'ed objects even when there are none * * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement - * -XX:+UnlockDiagnosticVMOptions -XX:+TraceDeoptimization + * -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:+TraceDeoptimization * compiler.uncommontrap.TraceDeoptimizationNoRealloc */ diff --git a/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java b/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java index ac3c4b0278a..8b2ee067140 100644 --- a/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java +++ b/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java @@ -38,17 +38,22 @@ public class AlignmentGapAccess { static class A { int fa; } static class B extends A { byte fb; } + static class C extends B { int fc; } static final long FA_OFFSET = UNSAFE.objectFieldOffset(A.class, "fa"); static final long FB_OFFSET = UNSAFE.objectFieldOffset(B.class, "fb"); + static final long FC_OFFSET = UNSAFE.objectFieldOffset(C.class, "fc"); static int test(B obj) { return UNSAFE.getInt(obj, FB_OFFSET + 1); } public static void main(String[] args) { + System.out.printf("Layout: +%d: fa; +%d: fb; +%d: fc\n", + FA_OFFSET, FB_OFFSET, FC_OFFSET); + for (int i = 0; i < 20_000; i++) { - test(new B()); + test(new C()); } System.out.println("TEST PASSED"); } diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeBooleanTest.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeBooleanTest.java new file mode 100644 index 00000000000..30058550af4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeBooleanTest.java @@ -0,0 +1,378 @@ +/* + * 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 compiler.unsafe; + +/* + * @test + * @bug 8385119 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.vm.annotation + * @run main/bootclasspath/othervm -Xbatch -DMODE=LSB -DMEMBAR=false ${test.main.class} + * @run main/bootclasspath/othervm -Xbatch -DMODE=LSB -DMEMBAR=true ${test.main.class} + */ + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Opcode; +import java.lang.constant.ClassDesc; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.util.*; + +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Stable; + +public class UnsafeBooleanTest { + static final Class THIS_CLASS = UnsafeBooleanTest.class; + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + // The regression test uses only mode LSB. + // See attached log files on JDK-8385119 for sample outputs from other modes. + enum Mode { + LSB("(x & 1)"), // Truncate to least significant bit + CTZ("(byte != 0)"), // Compare to zero + MIXED("(x & 1) + (byte != 0)"); // Truncate to LSB on stores, compare to zero on loads + + final String desc; + Mode(String desc) { this.desc = desc; } + } + + static final boolean VERBOSE = Boolean.getBoolean("VERBOSE"); + + // The memory barrier, if present, disrupts certain optimizations like store-to-load chaining. + // There must be no errors reported in either setting, but the "interesting" anomalies might change. + static final boolean MEMBAR = Boolean.parseBoolean(System.getProperty("MEMBAR", Boolean.TRUE.toString())); + + // Tested normalization mode + static final Mode MODE = Mode.valueOf(System.getProperty("MODE", Mode.LSB.toString())); + + static { + System.out.println("MODE=" + MODE.desc); + } + + // int testAllBoolean(Unsafe unsafe, Object base, long offset, int value) { + // unsafe.putBoolean(base, offset, value); + // if (MEMBAR) { unsafe.fullFence(); } + // return unsafe.getBoolean(base, offset); + // } + // That is, write a normalized 0/1 byte, then normalize again on read. This is the safest route. + static final MethodHandle TEST_ALL_BOOLEAN_MH = generateTestMethod(true, true); + + // int testPutBoolean(Unsafe unsafe, Object base, long offset, int value) { + // unsafe.putBoolean(base, offset, value); + // if (MEMBAR) { unsafe.fullFence(); } + // return unsafe.getByte(base, offset); + // } + // + // That is, write a normalized 0/1, then read whatever byte appeared in memory. + // This should behave perfectly, even if the incoming boolean was "dirty". + static final MethodHandle TEST_PUT_BOOLEAN_MH = generateTestMethod(false, true); + + // int testGetBoolean(Unsafe unsafe, Object base, long offset, int value) { + // unsafe.putByte(base, offset, value); + // if (MEMBAR) { unsafe.fullFence(); } + // return unsafe.getBoolean(base, offset); + // } + // + // That is, smash an arbitrary byte into memory (even if typed as a boolean), + // then read as a boolean, normalizing the read byte to 0/1. + // This models catastrophically poor use of unsafe, and is allowed to return + // non-normalized values (not 0 or 1), due to JIT optimizations of + // getBoolean. These optimizations assume (for Java heap booleans) + // that `x&1` can be optimized to just x because the loaded x is strongly + // typed (in the Java heap) as a boolean and must therefore be 0 or 1. + // This unit test detects a subversion of this invariant and reports, + // not an error, but an "interesting" case. + static final MethodHandle TEST_GET_BOOLEAN_MH = generateTestMethod(true, false); + + static boolean B; + static long J; + static @Stable boolean stableB; + static @Stable long stableJ; + + static final Random RANDOM = jdk.test.lib.Utils.getRandomInstance(); + + static final int[] INPUTS = new int[] { + 0, 1, + 2, 3, 4, 5, -1, + Byte.MIN_VALUE, Byte.MAX_VALUE, + Short.MIN_VALUE, Short.MAX_VALUE, + Character.MIN_VALUE , Character.MAX_VALUE, + Integer.MIN_VALUE, Integer.MAX_VALUE, + RANDOM.nextInt() + }; + + static final int[] TOGGLES = new int[] { 0, 1, 0xFF }; + + public static void main(String[] args) throws NoSuchFieldException { + runTestsOn("boolean[0]", new boolean[1], UNSAFE.arrayBaseOffset(boolean[].class)); + + // Mismatched array accesses. + runTestsOn("byte[0]", new byte[1], UNSAFE.arrayBaseOffset(byte[].class)); + runTestsOn("long[0]", new long[1], UNSAFE.arrayBaseOffset(long[].class)); + runTestsOn("long[0]+1", new long[1], UNSAFE.arrayBaseOffset(long[].class) + 1); + + runTestsOn("null", null, UNSAFE.allocateMemory(1)); + + Field booleanField = THIS_CLASS.getDeclaredField("B"); + runTestsOn("A.B", UNSAFE.staticFieldBase(booleanField), UNSAFE.staticFieldOffset(booleanField)); + + // Mismatched field accesses. + Field longField = THIS_CLASS.getDeclaredField("J"); + runTestsOn("A.J", UNSAFE.staticFieldBase(longField), UNSAFE.staticFieldOffset(longField)); + runTestsOn("A.J+1", UNSAFE.staticFieldBase(longField), UNSAFE.staticFieldOffset(longField) + 1); + + runTestsForConstantsOn("A.stableB", THIS_CLASS.getDeclaredField("stableB")); + runTestsForConstantsOn("A.stableJ", THIS_CLASS.getDeclaredField("stableJ")); + + if (!FAILURES.isEmpty()) { + throw new AssertionError("TEST FAILED"); + } + System.out.println("TEST PASSED"); + } + + static void runTestsForConstantsOn(String name, Field staticField) { + Object base = UNSAFE.staticFieldBase(staticField); + long offset = UNSAFE.staticFieldOffset(staticField); + + System.out.printf("Test: %s\n", name); + for (int input : INPUTS) { + for (int toggle : TOGGLES) { + int value = input ^ toggle; + // Prepare a dedicated test for each combination. Otherwise, constant folding would break test logic. + runTestsOn(prepare(name + " allBoolean", TEST_ALL_BOOLEAN_MH, base, offset), value, true); + runTestsOn(prepare(name + " putBoolean", TEST_PUT_BOOLEAN_MH, base, offset), value, true); + runTestsOn(prepare(name + " getBoolean", TEST_GET_BOOLEAN_MH, base, offset), value, false); + } + } + } + + static void runTestsOn(String name, Object base, long offset) { + Test[] getTests = prepare(name + " getBoolean", TEST_GET_BOOLEAN_MH, base, offset); + Test[] putTests = prepare(name + " putBoolean", TEST_PUT_BOOLEAN_MH, base, offset); + Test[] allTests = prepare(name + " allBoolean", TEST_ALL_BOOLEAN_MH, base, offset); + + System.out.printf("Test: %s\n", name); + for (int input : INPUTS) { + for (int toggle : TOGGLES) { + int value = input ^ toggle; + runTestsOn(allTests, value, true); + runTestsOn(putTests, value, true); + runTestsOn(getTests, value, false); + } + } + } + + static void runTestsOn(Test[] tests, int value, boolean normalizedOnStore) { + for (Test t : tests) { + runTest(t, value, normalizedOnStore); + } + } + + // Model what we expect the interpreter and/or JIT to do when accessing a boolean in memory. + // The `x!=0` behavior is historical, while `x&1` (truncation) is current. Note that + // JIT sometimes omit the normalization step, if the boolean in question is being READ from + // a Java heap variable that is strongly typed as a boolean. (Not an unsafely generated address, + // not off-heap.) When WRITING booleans to the Java heap, the interpreter and JIT both make sure + // to normalize as `x&1`, so the Java heap is never polluted (unless non-boolean accessor is used). + static int expected(int value, boolean normalizedOnStore) { + byte b = (byte) value; + int lsb = (b & 1); + int ctz = (b != 0 ? 1 : 0); + return switch (MODE) { + case CTZ -> ctz; + case LSB -> lsb; + case MIXED -> (normalizedOnStore ? lsb : ctz); + }; + } + + static void runTest(Test t, int value, boolean normalizedOnStore) { + int expected = expected(value, normalizedOnStore); + for (int iter = 0; iter < 20_000; iter++) { + try { + int r = t.test(value); + if (r != expected) { + if (!normalizedOnStore && (((byte) value) & 0xFF) == r) { + reportInterestingResult(t, value, r, expected); + } else if (reportFailure(t, value, r)) { + System.out.printf("FAILED: %s: 0x%08x: 0x%02x(%x) != %x\n", + TEST_NAMES.get(t), value, r, (r & 1), expected); + } + } else if (VERBOSE) { + reportInterestingResult(t, value, r, expected); + } + } catch (Throwable e) { + if (reportFailure(t, value, e)) { + System.out.printf("FAILED %s: 0x%08x: (throws %s) != %x\n", + TEST_NAMES.get(t), value, e, expected); + } + } + } + } + + static MethodHandle generateTestMethod(boolean isBooleanGetter, boolean isBooleanSetter) { + if (!isBooleanGetter && !isBooleanSetter) { + throw new InternalError("not supported"); + } + + final String name = !isBooleanGetter ? "testPutBoolean" : + !isBooleanSetter ? "testGetBoolean" : + "testAllBoolean"; + MethodType mt = MethodType.methodType(int.class /*rtype*/, + Unsafe.class, Object.class, long.class, int.class); + byte[] classFile = ClassFile.of().build(ClassDesc.of("compiler.unsafe.Helper"), + // static int test(Unsafe unsafe, Object base, long offset, int value) { + cb -> cb.withMethodBody(name, + mt.describeConstable().get(), + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> { + mb.aload(0); + mb.aload(1); + mb.lload(2); + mb.iload(4); + if (isBooleanSetter) { + // unsafe.putBoolean(base, offset, value); + MethodType putBooleanMT = MethodType.methodType(void.class, Object.class, long.class, boolean.class); + mb.invoke(Opcode.INVOKEVIRTUAL, Unsafe.class.describeConstable().get(), + "putBoolean", putBooleanMT.describeConstable().get(), false); + } else { + // unsafe.putByte(base, offset, value); + MethodType putByteMT = MethodType.methodType(void.class, Object.class, long.class, byte.class); + mb.invoke(Opcode.INVOKEVIRTUAL, Unsafe.class.describeConstable().get(), + "putByte", putByteMT.describeConstable().get(), false); + } + if (MEMBAR) { + // Issue a memory barrier to ensure no store-to-load forwarding between accesses happens. + mb.aload(0); + MethodType storeFenceMT = MethodType.methodType(void.class); + mb.invoke(Opcode.INVOKEVIRTUAL, Unsafe.class.describeConstable().get(), + "fullFence", storeFenceMT.describeConstable().get(), false); + } + mb.aload(0); + mb.aload(1); + mb.lload(2); + if (isBooleanGetter) { + // boolean b = unsafe.getBoolean(base, offset); + MethodType getBooleanMT = MethodType.methodType(boolean.class, Object.class, long.class); + mb.invoke(Opcode.INVOKEVIRTUAL, Unsafe.class.describeConstable().get(), + "getBoolean", getBooleanMT.describeConstable().get(), false); + } else { + // byte b = unsafe.getByte(base, offset); + MethodType getByteMT = MethodType.methodType(byte.class, Object.class, long.class); + mb.invoke(Opcode.INVOKEVIRTUAL, Unsafe.class.describeConstable().get(), + "getByte", getByteMT.describeConstable().get(), false); + } + // return b; + mb.ireturn(); + })); + try { + MethodHandles.Lookup lookup = MethodHandles.lookup().defineHiddenClass(classFile, true); + return lookup.findStatic(lookup.lookupClass(), name, mt); + } catch (IllegalAccessException | NoSuchMethodException e) { + throw new InternalError(e); + } + } + + record Result(int value, Object result) {} + + static final HashMap> FAILURES = new HashMap<>(); + + static boolean reportFailure(Test t, int value, Object result) { + var testFailures = FAILURES.computeIfAbsent(t, _ -> new HashSet<>()); + Result r = new Result(value, result); + boolean report = !testFailures.contains(r); + if (report) { + testFailures.add(r); + } + return report; + } + + // The "interesting" thing is an anomaly where a bad heap byte (poked in by Unsafe::putByte) comes back as a bad + // boolean (unsigned byte, no sign extension). When the anomaly is absent, the `x&1` normalization was applied + // by the interpreter or by the compiled code. When present, the compiler has optimized away the &1 in + // x&1, on the grounds that it clearly sees a load from a Java-heap boolean variable (field or array element), + // which can never ever be anything other than 0 or 1. Unless some buffoon called Unsafe::putByte to poke in + // something else. We are emulating such buffoons, to make sure their damage would be somewhat limited. + // + // This anomaly only occurs for a typed or constant reference to a container (instance or array) and a strongly-typed + // variable in that container (boolean field or element). Thus, if the container is off-heap, or the reference + // is not strongly typed, or if it is typed but the poked byte is in a non-boolean variable (a "mismatch"), + // then the x&1 normalization is retained in the compiled code. We test the strongly-typed case by + // binding the container object (instance or array) as a constant into the test loop (using insertArguments). + static final HashMap> INTERESTING = new HashMap<>(); + + static void reportInterestingResult(Test t, int value, int result, int expected) { + var s = INTERESTING.computeIfAbsent(t, _ -> new HashMap<>()); + Result r = new Result(value, result); + boolean report = !s.containsKey(r); + if (report) { + var msg = String.format("%-40s: 0x%08x: 0x%02x(%x) %s %x\n", + TEST_NAMES.get(t), value, result, (result & 1), + (result == expected ? "==" : "!="), expected); + s.put(r, msg); + System.out.printf("INTERESTING: %s", msg); + } + } + + public interface Test { + int test(int i) throws Throwable; + } + + static HashMap TEST_NAMES = new HashMap<>(); + + static Test register(String name, String suffix, Test t) { + TEST_NAMES.put(t, String.format("%s:%s", name, suffix)); + return t; + } + + static final MethodHandle MH_NON_NULL; + static { + try { + MH_NON_NULL = MethodHandles.lookup().findStatic(Objects.class, "requireNonNull", + MethodType.methodType(Object.class, Object.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new InternalError(e); + } + } + + static Test[] prepare(String id, MethodHandle test, Object base, long offset) { + final MethodHandle mhBOI = MethodHandles.insertArguments(test, 0, UNSAFE); + final MethodHandle mhOI = MethodHandles.insertArguments(mhBOI, 0, base); + final MethodHandle mhBI = MethodHandles.insertArguments(mhBOI, 1, offset); + final MethodHandle mhI = MethodHandles.insertArguments(mhBOI, 0, base, offset); + + ArrayList tests = new ArrayList<>(); + tests.add(register(id, "base+offset=const", i -> (int) mhI.invokeExact(i))); + tests.add(register(id, "offset=const", i -> (int) mhBI.invokeExact(base, i))); + tests.add(register(id, "base=const", i -> (int) mhOI.invokeExact(offset, i))); + tests.add(register(id, "noconst", i -> (int) mhBOI.invokeExact(base, offset, i))); + if (base != null) { + final MethodHandle mhBOIN = MethodHandles.filterArguments(mhBOI, 0, MH_NON_NULL); + tests.add(register(id, "base=nonnull", i -> (int) mhBOIN.invokeExact(base, offset, i))); + } + return tests.toArray(new Test[0]); + } +} diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeOffHeapBooleanTest.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeOffHeapBooleanTest.java index 202259b7bd7..7235febe4df 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeOffHeapBooleanTest.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeOffHeapBooleanTest.java @@ -42,7 +42,7 @@ public class UnsafeOffHeapBooleanTest { public static void test() { // Write two bytes to the off-heap memory location, both // bytes correspond to the boolean value 'true'. - UNSAFE.putShort(null, offHeapMemory, (short)0x0204); + UNSAFE.putShort(null, offHeapMemory, (short)0x03FF); // Read two bytes from the storage allocated above (as booleans). bool0 = UNSAFE.getBoolean(null, offHeapMemory + 0); diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeOnHeapBooleanTest.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeOnHeapBooleanTest.java index de6ae0c996c..f5ea69e35bf 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeOnHeapBooleanTest.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeOnHeapBooleanTest.java @@ -46,7 +46,7 @@ public class UnsafeOnHeapBooleanTest { Field staticVField = UnsafeOnHeapBooleanTest.class.getDeclaredField("static_v"); Object base = UNSAFE.staticFieldBase(staticVField); long offset = UNSAFE.staticFieldOffset(staticVField); - UNSAFE.putShort(base, offset, (short)0x0204); + UNSAFE.putShort(base, offset, (short)0x03FF); // Read two bytes from the static field // UnsafeOnHeapBooleanTest.static_v (as booleans). diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestCastIIToConvHF2FNoSp.java b/test/hotspot/jtreg/compiler/vectorapi/TestCastIIToConvHF2FNoSp.java new file mode 100644 index 00000000000..0c8aa58f586 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestCastIIToConvHF2FNoSp.java @@ -0,0 +1,113 @@ +/* + * Copyright 2025 Arm Limited and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 8370691 8373574 +* @summary Verify correct execution of CastII -> ConvHF2F IR sequence on AArch64 +* @modules jdk.incubator.vector +* @library /test/lib / +* @compile TestCastIIToConvHF2FNoSp.java +* @run driver/timeout=480 compiler.vectorapi.TestCastIIToConvHF2FNoSp +*/ + +package compiler.vectorapi; +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import static jdk.incubator.vector.Float16.*; +import static java.lang.Float.*; +import java.util.Arrays; +import jdk.test.lib.*; +import compiler.lib.generators.Generator; +import static compiler.lib.generators.Generators.G; + +public class TestCastIIToConvHF2FNoSp { + short[] input1; + short[] output; + static final int LEN = 527; + + static final Float16 FP16_CONST = Float16.valueOf(1023.0f); + static final VectorSpecies SPECIES = Float16Vector.SPECIES_PREFERRED; + + public static void main(String args[]) { + // Test with default MaxVectorSize + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + + // Test with different values of MaxVectorSize + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=8"); + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=16"); + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=32"); + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=64"); + } + + static void assertResults(int arity, short ... values) { + assert values.length == (arity + 2); + Float16 expected_fp16 = shortBitsToFloat16(values[arity]); + Float16 actual_fp16 = shortBitsToFloat16(values[arity + 1]); + if(!expected_fp16.equals(actual_fp16)) { + String inputs = Arrays.toString(Arrays.copyOfRange(values, 0, arity - 1)); + throw new AssertionError("Result Mismatch!, input = " + inputs + " actual = " + actual_fp16 + " expected = " + expected_fp16); + } + } + + public TestCastIIToConvHF2FNoSp() { + input1 = new short[LEN]; + output = new short[LEN]; + + Generator gen = G.float16s(); + for (int i = 0; i < LEN; ++i) { + input1[i] = gen.next(); + } + } + + @Test + @IR(counts = {IRNode.MIN_VHF, " >0 "}, + applyIfCPUFeature = {"sve", "true"}) + @IR(counts = {IRNode.MIN_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true", "sve", "false"}) + void vectorMinConstantInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.MIN, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.MIN, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMinConstantInputFloat16") + void checkResultMinConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(Math.min(FP16_CONST.floatValue(), float16ToFloat(input1[i]))); + assertResults(2, float16ToRawShortBits(FP16_CONST), input1[i], expected, output[i]); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java new file mode 100644 index 00000000000..9cf92af2644 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java @@ -0,0 +1,60 @@ +/* + * 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 compiler.vectorapi; + +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; + +import java.util.Random; + +/* + * @test + * @bug 8387698 + * @summary C2 VectorAPI: Float16Vector::indexInRange hits fatal error: Not monotonic. + * @modules jdk.incubator.vector + * @requires vm.debug == true + * @run main ${test.main.class} + * @run main/othervm -XX:-UncommonNullCast -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ +public class TestFloat16VectorConvergence { + + static Object test(long offset, long limit) { + boolean[] array = new boolean[16]; + var v = VectorMask.fromArray(Float16Vector.SPECIES_256, array, 0); + return v.indexInRange(offset, limit); + } + + public static void main(String[] args) { + Random random = new Random(0); + for (int i = 0; i < 10_000; i++) { + // A negative offset drives indexInRange into indexPartiallyInRange, + // which is where the un-foldable Float16 mask unbox is produced. + Object mask = test(-70368744177664L, random.nextLong()); + if (mask == null) { + throw new AssertionError("Unexpected null result from indexInRange"); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java new file mode 100644 index 00000000000..30d066d1b15 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java @@ -0,0 +1,620 @@ +/* + * 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 8370691 +* @summary Test intrinsification of Float16Vector operations +* @modules jdk.incubator.vector +* @library /test/lib / +* @compile TestFloat16VectorOperations.java +* @run driver/timeout=480 compiler.vectorapi.TestFloat16VectorOperations +*/ + +package compiler.vectorapi; +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import static jdk.incubator.vector.Float16.*; +import static java.lang.Float.*; +import java.util.Arrays; +import jdk.test.lib.*; +import compiler.lib.generators.Generator; +import static compiler.lib.generators.Generators.G; + +public class TestFloat16VectorOperations { + short[] input1; + short[] input2; + short[] input3; + short[] output; + static final int LEN = 527; + static short FP16_SCALAR = (short)0x7777; + + static final Float16 FP16_CONST = Float16.valueOf(1023.0f); + static final VectorSpecies SPECIES = Float16Vector.SPECIES_PREFERRED; + + public static void main(String args[]) { + // Test with default MaxVectorSize + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + + // Test with different values of MaxVectorSize + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=8"); + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=16"); + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=32"); + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=64"); + } + + static void assertResults(int arity, short ... values) { + assert values.length == (arity + 2); + Float16 expected_fp16 = shortBitsToFloat16(values[arity]); + Float16 actual_fp16 = shortBitsToFloat16(values[arity + 1]); + if(!expected_fp16.equals(actual_fp16)) { + String inputs = Arrays.toString(Arrays.copyOfRange(values, 0, arity - 1)); + throw new AssertionError("Result Mismatch!, input = " + inputs + " actual = " + actual_fp16 + " expected = " + expected_fp16); + } + } + + public TestFloat16VectorOperations() { + input1 = new short[LEN]; + input2 = new short[LEN]; + input3 = new short[LEN]; + output = new short[LEN]; + + Generator gen = G.float16s(); + for (int i = 0; i < LEN; ++i) { + input1[i] = gen.next(); + input2[i] = gen.next(); + input3[i] = gen.next(); + } + } + + @Test + @IR(counts = {IRNode.ADD_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.ADD_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorAddFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.ADD, + Float16Vector.fromArray(SPECIES, input2, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.ADD, + Float16Vector.fromArray(SPECIES, input2, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorAddFloat16") + void checkResultAdd() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input1[i]) + float16ToFloat(input2[i])); + assertResults(2, input1[i], input2[i], expected, output[i]); + } + } + + + @Test + @IR(counts = {IRNode.SUB_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.SUB_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorSubFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.SUB, + Float16Vector.fromArray(SPECIES, input2, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.SUB, + Float16Vector.fromArray(SPECIES, input2, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorSubFloat16") + void checkResultSub() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input1[i]) - float16ToFloat(input2[i])); + assertResults(2, input1[i], input2[i], expected, output[i]); + } + } + + + @Test + @IR(counts = {IRNode.MUL_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.MUL_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorMulFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.MUL, + Float16Vector.fromArray(SPECIES, input2, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.MUL, + Float16Vector.fromArray(SPECIES, input2, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMulFloat16") + void checkResultMul() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input1[i]) * float16ToFloat(input2[i])); + assertResults(2, input1[i], input2[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.DIV_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.DIV_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorDivFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.DIV, + Float16Vector.fromArray(SPECIES, input2, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.DIV, + Float16Vector.fromArray(SPECIES, input2, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorDivFloat16") + void checkResultDiv() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input1[i]) / float16ToFloat(input2[i])); + assertResults(2, input1[i], input2[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.MIN_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.MIN_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorMinFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.MIN, + Float16Vector.fromArray(SPECIES, input2, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.MIN, + Float16Vector.fromArray(SPECIES, input2, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMinFloat16") + void checkResultMin() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(Math.min(float16ToFloat(input1[i]), float16ToFloat(input2[i]))); + assertResults(2, input1[i], input2[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.MAX_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.MAX_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorMaxFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.MAX, + Float16Vector.fromArray(SPECIES, input2, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.MAX, + Float16Vector.fromArray(SPECIES, input2, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMaxFloat16") + void checkResultMax() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(Math.max(float16ToFloat(input1[i]), float16ToFloat(input2[i]))); + assertResults(2, input1[i], input2[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.SQRT_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.SQRT_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorSqrtFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.SQRT) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.SQRT) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorSqrtFloat16") + void checkResultSqrt() { + for (int i = 0; i < LEN; ++i) { + short expected = float16ToRawShortBits(sqrt(shortBitsToFloat16(input1[i]))); + assertResults(1, input1[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorFmaFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(SPECIES, input2, i), + Float16Vector.fromArray(SPECIES, input3, i)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(SPECIES, input2, i, mask), + Float16Vector.fromArray(SPECIES, input3, i, mask)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorFmaFloat16") + void checkResultFma() { + for (int i = 0; i < LEN; ++i) { + short expected = float16ToRawShortBits(fma(shortBitsToFloat16(input1[i]), shortBitsToFloat16(input2[i]), + shortBitsToFloat16(input3[i]))); + assertResults(3, input1[i], input2[i], input3[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorFmaFloat16ScalarMixedConstants() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.FMA, + FP16_SCALAR, + floatToFloat16(3.0f)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.FMA, + FP16_SCALAR, + floatToFloat16(3.0f)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorFmaFloat16ScalarMixedConstants") + void checkResultFmaScalarMixedConstants() { + for (int i = 0; i < LEN; ++i) { + short expected = float16ToRawShortBits(fma(shortBitsToFloat16(input1[i]), shortBitsToFloat16(FP16_SCALAR), + shortBitsToFloat16(floatToFloat16(3.0f)))); + assertResults(2, input1[i], FP16_SCALAR, expected, output[i]); + } + } + + + @Test + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorFmaFloat16MixedConstants() { + short input3 = floatToFloat16(3.0f); + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(SPECIES, input2, i), + input3) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(SPECIES, input2, i, mask), + input3) + .intoArray(output, i, mask); + } + } + + + @Check(test="vectorFmaFloat16MixedConstants") + void checkResultFmaMixedConstants() { + short input3 = floatToFloat16(3.0f); + for (int i = 0; i < LEN; ++i) { + short expected = float16ToRawShortBits(fma(shortBitsToFloat16(input1[i]), shortBitsToFloat16(input2[i]), shortBitsToFloat16(input3))); + assertResults(3, input1[i], input2[i], input3, expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.FMA_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorFmaFloat16AllConstants() { + short input1 = floatToFloat16(1.0f); + short input2 = floatToFloat16(2.0f); + short input3 = floatToFloat16(3.0f); + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, input1) + .lanewise(VectorOperators.FMA, + input2, + input3) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.broadcast(SPECIES, input1) + .lanewise(VectorOperators.FMA, + input2, + input3) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorFmaFloat16AllConstants") + void checkResultFmaAllConstants() { + short input1 = floatToFloat16(1.0f); + short input2 = floatToFloat16(2.0f); + short input3 = floatToFloat16(3.0f); + for (int i = 0; i < LEN; ++i) { + short expected = float16ToRawShortBits(fma(shortBitsToFloat16(input1), shortBitsToFloat16(input2), shortBitsToFloat16(input3))); + assertResults(3, input1, input2, input3, expected, output[i]); + } + } + + + @Test + @IR(counts = {IRNode.ADD_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zvfh", "true", "sve", "true"}) + @IR(counts = {IRNode.ADD_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorAddConstInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.ADD, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.ADD, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorAddConstInputFloat16") + void checkResultAddConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input1[i]) + FP16_CONST.floatValue()); + assertResults(2, input1[i], float16ToRawShortBits(FP16_CONST), expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.SUB_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "sve", "true"}) + @IR(counts = {IRNode.SUB_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorSubConstInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input1, i) + .lanewise(VectorOperators.SUB, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input1, i, mask) + .lanewise(VectorOperators.SUB, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorSubConstInputFloat16") + void checkResultSubConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input1[i]) - FP16_CONST.floatValue()); + assertResults(2, input1[i], float16ToRawShortBits(FP16_CONST), expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.MUL_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "sve", "true"}) + @IR(counts = {IRNode.MUL_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorMulConstantInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input2, i) + .lanewise(VectorOperators.MUL, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input2, i, mask) + .lanewise(VectorOperators.MUL, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMulConstantInputFloat16") + void checkResultMulConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(FP16_CONST.floatValue() * float16ToFloat(input2[i])); + assertResults(2, float16ToRawShortBits(FP16_CONST), input2[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.DIV_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "sve", "true"}) + @IR(counts = {IRNode.DIV_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorDivConstantInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input2, i) + .lanewise(VectorOperators.DIV, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input2, i, mask) + .lanewise(VectorOperators.DIV, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorDivConstantInputFloat16") + void checkResultDivConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(float16ToFloat(input2[i]) / FP16_CONST.floatValue()); + assertResults(2, input2[i], float16ToRawShortBits(FP16_CONST), expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.MAX_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "sve", "true"}) + @IR(counts = {IRNode.MAX_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorMaxConstantInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input2, i) + .lanewise(VectorOperators.MAX, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input2, i, mask) + .lanewise(VectorOperators.MAX, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMaxConstantInputFloat16") + void checkResultMaxConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(Math.max(FP16_CONST.floatValue(), float16ToFloat(input2[i]))); + assertResults(2, float16ToRawShortBits(FP16_CONST), input2[i], expected, output[i]); + } + } + + @Test + @IR(counts = {IRNode.MIN_VHF, " >0 "}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "sve", "true"}) + @IR(counts = {IRNode.MIN_VHF, " >0 "}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + void vectorMinConstantInputFloat16() { + int i = 0; + for (; i < SPECIES.loopBound(LEN); i += SPECIES.length()) { + Float16Vector.fromArray(SPECIES, input2, i) + .lanewise(VectorOperators.MIN, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i); + } + if (i < LEN) { + VectorMask mask = SPECIES.indexInRange(i, LEN); + Float16Vector.fromArray(SPECIES, input2, i, mask) + .lanewise(VectorOperators.MIN, + float16ToRawShortBits(FP16_CONST)) + .intoArray(output, i, mask); + } + } + + @Check(test="vectorMinConstantInputFloat16") + void checkResultMinConstantInputFloat16() { + for (int i = 0; i < LEN; ++i) { + short expected = floatToFloat16(Math.min(FP16_CONST.floatValue(), float16ToFloat(input2[i]))); + assertResults(2, float16ToRawShortBits(FP16_CONST), input2[i], expected, output[i]); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java b/test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java new file mode 100644 index 00000000000..f24d2d9d087 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java @@ -0,0 +1,80 @@ +/* + * 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 compiler.vectorapi; + +import jdk.incubator.vector.*; + +/* + * @test id=vanilla + * @bug 8387411 + * @modules jdk.incubator.vector + * + * @run driver ${test.main.class} + */ + +/* + * @test id=KNL + * @bug 8387411 + * @modules jdk.incubator.vector + * + * @run main/othervm -Xbatch + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+UseKNLSetting + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ + +public class TestMaskUnboxingTypeMismatch { + + public static Object pollute() { + VectorMask intMask = VectorMask.fromLong(IntVector.SPECIES_512, 1L); + // Profile "andNot" with I512. + return intMask.andNot(intMask); + } + + public static Object test() { + var v0 = ByteVector.broadcast(ByteVector.SPECIES_128, (byte)7); + var v1 = VectorMask.fromLong(ByteVector.SPECIES_128, 1L); + var v2 = VectorMask.fromLong(ByteVector.SPECIES_128, 2L); + // Use "andNot" with B128. + // We can get some boxing of B128 mask, which is later unboxed + // as profiled I512, which is impossible. When trying to insert + // an VectorMaskCast in VectorUnboxNode::Ideal, we hit an assert, + // because with UseKNLSetting, B128 mask is a NVectMask, and I512 + // a PVectMask. + var v3 = v1.andNot(v2); + var v4 = v0.lanewise(VectorOperators.UMAX, (byte)42, v3); + return v4; + } + + public static void main(String[] args) { + // Sufficient repetitions to get some profiling. + for (int i = 0; i < 10_000; i++) { + pollute(); + } + // Sufficient repetitions to get compilation. + for (int i = 0; i < 50_000; i++) { + test(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java new file mode 100644 index 00000000000..6abf88ed06f --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java @@ -0,0 +1,84 @@ +/* + * 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 8386163 + * @summary Checks there is no assertion failure with macro logic optimization when both inputs of not patterns are all one vectors + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorSpecies; + +public class TestMaskedNotAllOnes { + + private static final VectorSpecies ISP = IntVector.SPECIES_PREFERRED; + private static final int VALUE = 1234567; + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + @Test + @Warmup(10000) + static int[] testMaskedDivNegOne() { + IntVector v = IntVector.broadcast(ISP, VALUE); + VectorMask mask = VectorMask.fromLong(ISP, -1L); + int[] out = new int[ISP.length()]; + v.div(-1, mask).intoArray(out, 0); + return out; + } + + static final int[] GOLD_DIV = testMaskedDivNegOne(); + + @Check(test = "testMaskedDivNegOne") + static void checkMaskedDivNegOne(int[] out) { + Verify.checkEQ(GOLD_DIV, out); + } + + @Test + @Warmup(10000) + static int[] testMaskedNotAllOnesVector() { + IntVector allOnes = IntVector.broadcast(ISP, -1); + VectorMask mask = VectorMask.fromLong(ISP, -1L); + int[] out = new int[ISP.length()]; + allOnes.lanewise(VectorOperators.NOT, mask).intoArray(out, 0); + return out; + } + + static final int[] GOLD_NOT = testMaskedNotAllOnesVector(); + + @Check(test = "testMaskedNotAllOnesVector") + static void checkMaskedNotAllOnesVector(int[] out) { + Verify.checkEQ(GOLD_NOT, out); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java new file mode 100644 index 00000000000..7c30e439515 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java @@ -0,0 +1,142 @@ +/* + * 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 id=SVE + * @bug 8387149 + * @summary Test case for SelectFromTwoVector with index operand same as other inputs. + * @requires vm.compiler2.enabled + * @requires os.arch == "aarch64" & vm.cpu.features ~= ".*sve.*" + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+UnlockDiagnosticVMOptions + * -XX:UseSVE=1 + * -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=dontinline,${test.main.class}::test* + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ + +/* + * @test id=NEON + * @bug 8387149 + * @summary Test case for SelectFromTwoVector with index operand same as other inputs. + * @requires vm.compiler2.enabled + * @requires os.arch == "aarch64" & vm.cpu.features ~= ".*asimd.*" + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+UnlockDiagnosticVMOptions + * -XX:UseSVE=0 + * -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=dontinline,${test.main.class}::test* + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ + +package compiler.vectorapi; + +import java.util.Random; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +public class TestSelectFromTwoVectorSameOperand { + static final int SIZE = 8; + + static byte[] byte_input1 = new byte[SIZE]; + static byte[] byte_input2 = new byte[SIZE]; + static byte[] byte_output = new byte[SIZE]; + static final byte byte_index_mask = 15; + + static short[] short_input1 = new short[SIZE / 2]; + static short[] short_input2 = new short[SIZE / 2]; + static short[] short_output = new short[SIZE / 2]; + static final short short_index_mask = 7; + + static { + Random r = new Random(42); + r.nextBytes(byte_input1); + r.nextBytes(byte_input2); + + for (int i = 0; i < SIZE / 2; i++) { + short_input1[i] = byte_input1[i]; + short_input2[i] = byte_input2[i]; + } + } + + public static void main(String[] args) { + for (int i = 0; i < 100_000; ++i) { + test_byte_src1(); + verify_byte(byte_input1, byte_input2, byte_input1, byte_output); + test_byte_src2(); + verify_byte(byte_input1, byte_input2, byte_input2, byte_output); + test_short_src1(); + verify_short(short_input1, short_input2, short_input1, short_output); + test_short_src2(); + verify_short(short_input1, short_input2, short_input2, short_output); + } + } + + static void test_byte_src1() { + ByteVector src1 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input1, 0).and(byte_index_mask); + ByteVector src2 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input2, 0).and(byte_index_mask); + src1.selectFrom(src1, src2).intoArray(byte_output, 0); + } + + static void test_byte_src2() { + ByteVector src1 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input1, 0).and(byte_index_mask); + ByteVector src2 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input2, 0).and(byte_index_mask); + src2.selectFrom(src1, src2).intoArray(byte_output, 0); + } + + static void test_short_src1() { + ShortVector src1 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input1, 0).and(short_index_mask); + ShortVector src2 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input2, 0).and(short_index_mask); + src1.selectFrom(src1, src2).intoArray(short_output, 0); + } + + static void test_short_src2() { + ShortVector src1 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input1, 0).and(short_index_mask); + ShortVector src2 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input2, 0).and(short_index_mask); + src2.selectFrom(src1, src2).intoArray(short_output, 0); + } + + static void verify_byte(byte[] src1, byte[] src2, byte[] index, byte[] output) { + for (int i = 0; i < SIZE; i++) { + int index_value = index[i] & byte_index_mask; + byte element_value = (index_value < SIZE) ? src1[index_value] : src2[index_value - SIZE]; + byte masked_element_value = (byte) (element_value & byte_index_mask); + Asserts.assertEQ(masked_element_value, output[i]); + } + } + + static void verify_short(short[] src1, short[] src2, short[] index, short[] output) { + for (int i = 0; i < SIZE / 2; i++) { + int index_value = index[i] & short_index_mask; + short element_value = (index_value < SIZE / 2) ? src1[index_value] : src2[index_value - SIZE / 2]; + short masked_element_value = (short) (element_value & short_index_mask); + Asserts.assertEQ(masked_element_value, output[i]); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java b/test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java new file mode 100644 index 00000000000..2c4b10e7e2b --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java @@ -0,0 +1,447 @@ +/* + * 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 8386155 + * @summary Test missing trunctation after subword vector operation reassociation + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver compiler.vectorapi.TestTruncationAfterReassociation + */ + +package compiler.vectorapi; + +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.*; + +public class TestTruncationAfterReassociation { + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + static final VectorSpecies BSP = ByteVector.SPECIES_PREFERRED; + static final VectorSpecies SSP = ShortVector.SPECIES_PREFERRED; + + // Random value source (covers the full integer range, biased towards + // interesting/special values such as 0, MIN, MAX and powers of two). + static final Generator INT_GEN = Generators.G.ints(); + + static final int RAND_ITERS = 2048; + + static byte B_127 = (byte) 127; + static byte B_N16 = (byte) -16; + static byte B_N7 = (byte) -7; + static byte B_100 = (byte) 100; + static byte B_4 = (byte) 4; + static byte B_5 = (byte) 5; + static byte B_10 = (byte) 10; + static byte B_N128 = (byte) -128; + static byte B_1 = (byte) 1; + + static short S_32767 = (short) 32767; + static short S_N16 = (short) -16; + static short S_N7 = (short) -7; + static short S_200 = (short) 200; + static short S_5 = (short) 5; + static short S_10 = (short) 10; + static short S_N32768 = (short) -32768; + static short S_1 = (short) 1; + + static byte bmul(byte x, byte y) { return (byte) (x * y); } + static byte badd(byte x, byte y) { return (byte) (x + y); } + static byte bsub(byte x, byte y) { return (byte) (x - y); } + static byte bmax(byte x, byte y) { return (byte) Math.max(x, y); } + static byte bmin(byte x, byte y) { return (byte) Math.min(x, y); } + + static short smul(short x, short y) { return (short) (x * y); } + static short sadd(short x, short y) { return (short) (x + y); } + static short ssub(short x, short y) { return (short) (x - y); } + static short smax(short x, short y) { return (short) Math.max(x, y); } + static short smin(short x, short y) { return (short) Math.min(x, y); } + + @Test + static byte bug_8386155_reproducer() { + return ByteVector.broadcast(ByteVector.SPECIES_64, (byte) 127) + // Expected: mul is truncated to signed byte: 127 * -16 = (byte)-2032 = 16 + .mul((byte) -16) + // Expected: max(16, -7) = 16 + .max((byte) -7) + .lane(0); + } + + @Run(test = "bug_8386155_reproducer") + static void run_bug_8386155_reproducer() { + Verify.checkEQ(bug_8386155_reproducer(), (byte) 16); + } + + /* ========================================================= + * BYTE: then . + * ========================================================= */ + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_mul_then_max() { + return ByteVector.broadcast(BSP, B_127) + .mul(ByteVector.broadcast(BSP, B_N16)) + .max(ByteVector.broadcast(BSP, B_N7)) + .lane(0); + } + + @Run(test = "byte_mul_then_max") + static void run_byte_mul_then_max() { + Verify.checkEQ(byte_mul_then_max(), bmax(bmul(B_127, B_N16), B_N7)); + } + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_mul_then_min() { + return ByteVector.broadcast(BSP, B_100) + .mul(ByteVector.broadcast(BSP, B_4)) + .min(ByteVector.broadcast(BSP, B_5)) + .lane(0); + } + + @Run(test = "byte_mul_then_min") + static void run_byte_mul_then_min() { + Verify.checkEQ(byte_mul_then_min(), bmin(bmul(B_100, B_4), B_5)); + } + + @Test + @IR(failOn = { IRNode.ADD_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_add_then_max() { + return ByteVector.broadcast(BSP, B_127) + .add(ByteVector.broadcast(BSP, B_127)) + .max(ByteVector.broadcast(BSP, B_5)) + .lane(0); + } + + @Run(test = "byte_add_then_max") + static void run_byte_add_then_max() { + Verify.checkEQ(byte_add_then_max(), bmax(badd(B_127, B_127), B_5)); + } + + @Test + @IR(failOn = { IRNode.ADD_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_add_then_min() { + return ByteVector.broadcast(BSP, B_127) + .add(ByteVector.broadcast(BSP, B_127)) + .min(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_add_then_min") + static void run_byte_add_then_min() { + Verify.checkEQ(byte_add_then_min(), bmin(badd(B_127, B_127), B_10)); + } + + @Test + @IR(failOn = { IRNode.SUB_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.SUB_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_sub_then_max() { + return ByteVector.broadcast(BSP, B_N128) + .sub(ByteVector.broadcast(BSP, B_1)) + .max(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_sub_then_max") + static void run_byte_sub_then_max() { + Verify.checkEQ(byte_sub_then_max(), bmax(bsub(B_N128, B_1), B_10)); + } + + @Test + @IR(failOn = { IRNode.SUB_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.SUB_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_sub_then_min() { + return ByteVector.broadcast(BSP, B_N128) + .sub(ByteVector.broadcast(BSP, B_1)) + .min(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_sub_then_min") + static void run_byte_sub_then_min() { + Verify.checkEQ(byte_sub_then_min(), bmin(bsub(B_N128, B_1), B_10)); + } + + /* ========================================================= + * SHORT: then . + * ========================================================= */ + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MAX_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_mul_then_max() { + return ShortVector.broadcast(SSP, S_32767) + .mul(ShortVector.broadcast(SSP, S_N16)) + .max(ShortVector.broadcast(SSP, S_N7)) + .lane(0); + } + + @Run(test = "short_mul_then_max") + static void run_short_mul_then_max() { + Verify.checkEQ(short_mul_then_max(), smax(smul(S_32767, S_N16), S_N7)); + } + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_mul_then_min() { + return ShortVector.broadcast(SSP, S_200) + .mul(ShortVector.broadcast(SSP, S_200)) + .min(ShortVector.broadcast(SSP, S_5)) + .lane(0); + } + + @Run(test = "short_mul_then_min") + static void run_short_mul_then_min() { + Verify.checkEQ(short_mul_then_min(), smin(smul(S_200, S_200), S_5)); + } + + @Test + @IR(failOn = { IRNode.ADD_VS, IRNode.MAX_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_add_then_max() { + return ShortVector.broadcast(SSP, S_32767) + .add(ShortVector.broadcast(SSP, S_32767)) + .max(ShortVector.broadcast(SSP, S_5)) + .lane(0); + } + + @Run(test = "short_add_then_max") + static void run_short_add_then_max() { + Verify.checkEQ(short_add_then_max(), smax(sadd(S_32767, S_32767), S_5)); + } + + @Test + @IR(failOn = { IRNode.SUB_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.SUB_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_sub_then_min() { + return ShortVector.broadcast(SSP, S_N32768) + .sub(ShortVector.broadcast(SSP, S_1)) + .min(ShortVector.broadcast(SSP, S_10)) + .lane(0); + } + + @Run(test = "short_sub_then_min") + static void run_short_sub_then_min() { + Verify.checkEQ(short_sub_then_min(), smin(ssub(S_N32768, S_1), S_10)); + } + + // Two independent overflowing products feeding a single max + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_two_muls_then_max() { + return ByteVector.broadcast(BSP, B_127).mul(ByteVector.broadcast(BSP, B_N16)) + .max(ByteVector.broadcast(BSP, B_100).mul(ByteVector.broadcast(BSP, B_4))) + .lane(0); + } + + @Run(test = "byte_two_muls_then_max") + static void run_byte_two_muls_then_max() { + Verify.checkEQ(byte_two_muls_then_max(), + bmax(bmul(B_127, B_N16), bmul(B_100, B_4))); + } + + // Chained (reassociated) adds whose running value overflows, then a max + @Test + @IR(failOn = { IRNode.ADD_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_chain_add_then_max() { + return ByteVector.broadcast(BSP, B_127) + .add(ByteVector.broadcast(BSP, B_127)) + .add(ByteVector.broadcast(BSP, B_127)) + .max(ByteVector.broadcast(BSP, B_127)) + .lane(0); + } + + @Run(test = "byte_chain_add_then_max") + static void run_byte_chain_add_then_max() { + Verify.checkEQ(byte_chain_add_then_max(), + bmax(badd(badd(B_127, B_127), B_127), B_127)); + } + + // Overflowing product feeding max then min: + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_mul_max_then_min() { + return ByteVector.broadcast(BSP, B_127) + .mul(ByteVector.broadcast(BSP, B_N16)) + .max(ByteVector.broadcast(BSP, B_N7)) + .min(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_mul_max_then_min") + static void run_byte_mul_max_then_min() { + Verify.checkEQ(byte_mul_max_then_min(), + bmin(bmax(bmul(B_127, B_N16), B_N7), B_10)); + } + + // Two overflowing short products feeding a single min + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_two_muls_then_min() { + return ShortVector.broadcast(SSP, S_200).mul(ShortVector.broadcast(SSP, S_200)) + .min(ShortVector.broadcast(SSP, S_32767).mul(ShortVector.broadcast(SSP, S_N16))) + .lane(0); + } + + @Run(test = "short_two_muls_then_min") + static void run_short_two_muls_then_min() { + Verify.checkEQ(short_two_muls_then_min(), + smin(smul(S_200, S_200), smul(S_32767, S_N16))); + } + + /* ========================================================= + * Randomized coverage (Generators). + * ========================================================= */ + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte rand_byte_mul_then_max(byte a, byte b, byte c) { + return ByteVector.broadcast(BSP, a) + .mul(ByteVector.broadcast(BSP, b)) + .max(ByteVector.broadcast(BSP, c)) + .lane(0); + } + + @Run(test = "rand_byte_mul_then_max") + static void run_rand_byte_mul_then_max() { + for (int i = 0; i < RAND_ITERS; i++) { + byte a = INT_GEN.next().byteValue(); + byte b = INT_GEN.next().byteValue(); + byte c = INT_GEN.next().byteValue(); + Verify.checkEQ(rand_byte_mul_then_max(a, b, c), bmax(bmul(a, b), c)); + } + } + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte rand_byte_mul_then_min(byte a, byte b, byte c) { + return ByteVector.broadcast(BSP, a) + .mul(ByteVector.broadcast(BSP, b)) + .min(ByteVector.broadcast(BSP, c)) + .lane(0); + } + + @Run(test = "rand_byte_mul_then_min") + static void run_rand_byte_mul_then_min() { + for (int i = 0; i < RAND_ITERS; i++) { + byte a = INT_GEN.next().byteValue(); + byte b = INT_GEN.next().byteValue(); + byte c = INT_GEN.next().byteValue(); + Verify.checkEQ(rand_byte_mul_then_min(a, b, c), bmin(bmul(a, b), c)); + } + } + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MAX_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short rand_short_mul_then_max(short a, short b, short c) { + return ShortVector.broadcast(SSP, a) + .mul(ShortVector.broadcast(SSP, b)) + .max(ShortVector.broadcast(SSP, c)) + .lane(0); + } + + @Run(test = "rand_short_mul_then_max") + static void run_rand_short_mul_then_max() { + for (int i = 0; i < RAND_ITERS; i++) { + short a = INT_GEN.next().shortValue(); + short b = INT_GEN.next().shortValue(); + short c = INT_GEN.next().shortValue(); + Verify.checkEQ(rand_short_mul_then_max(a, b, c), smax(smul(a, b), c)); + } + } + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short rand_short_mul_then_min(short a, short b, short c) { + return ShortVector.broadcast(SSP, a) + .mul(ShortVector.broadcast(SSP, b)) + .min(ShortVector.broadcast(SSP, c)) + .lane(0); + } + + @Run(test = "rand_short_mul_then_min") + static void run_rand_short_mul_then_min() { + for (int i = 0; i < RAND_ITERS; i++) { + short a = INT_GEN.next().shortValue(); + short b = INT_GEN.next().shortValue(); + short c = INT_GEN.next().shortValue(); + Verify.checkEQ(rand_short_mul_then_min(a, b, c), smin(smul(a, b), c)); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java b/test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java new file mode 100644 index 00000000000..8d4a580efb8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java @@ -0,0 +1,51 @@ +/* + * 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 compiler.vectorapi; + +import jdk.incubator.vector.ByteVector; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorShuffle; + +/* + * @test + * @bug 8387012 + * @summary Expansion of a VectorUnboxNode should not create a type-unsafe load. + * @modules jdk.incubator.vector + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -Xbatch -XX:-TieredCompilation + * -XX:+StressGCM -XX:+StressIGVN -XX:+StressCCP ${test.main.class} + */ +public class TestTypeUnsafeLoad { + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test(); + } + } + + public static ByteVector test() { + var v0 = ByteVector.broadcast(ByteVector.SPECIES_128, (byte) 0); + var v2 = v0.rearrange(VectorShuffle.makeUnzip(ByteVector.SPECIES_128, 1)); + var v3 = v0.lanewise(VectorOperators.MIN, v2); + var v5 = v3.lanewise(VectorOperators.MAX, v0); + return v5; + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java new file mode 100644 index 00000000000..cb58cfca652 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java @@ -0,0 +1,459 @@ +/* + * 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 compiler.vectorapi; + +import jdk.incubator.vector.*; +import static jdk.incubator.vector.VectorOperators.AND; +import static jdk.incubator.vector.VectorOperators.MUL; +import static jdk.incubator.vector.VectorOperators.LSHR; +import static jdk.incubator.vector.VectorOperators.ASHR; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import compiler.lib.verify.*; + +/* + * @test + * @bug 8384963 8383905 + * @key randomness + * @summary C2: Incorrect uint constant match mishandles negative values in vectors + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver compiler.vectorapi.TestVectorMulLongToSignedUnsignedInt + */ +public class TestVectorMulLongToSignedUnsignedInt { + static final VectorSpecies SPECIES = LongVector.SPECIES_PREFERRED; + static final int SIZE = SPECIES.length(); + + private static final Generators RD = Generators.G; + + static final long[] src1 = new long[SIZE]; + static final long[] src2 = new long[SIZE]; + static long[] res = new long[SIZE]; + + static final boolean[] mask_arr = new boolean[SIZE]; + static final VectorMask MASK; + + // Random compile-time-constant masks. + static final long RAND_MASK1 = RD.longs().next(); + static final long RAND_MASK2 = RD.longs().next(); + + // Random compile-time-constant shift count in [0, 63]. A shift >= 32 clears + // the upper doubleword (fits uint); a smaller shift may not. + static final int RAND_SHIFT1 = RD.ints().next() & 0x3F; + + // Random input arrays for the random-mask correctness cases. + static final long[] rsrc1 = new long[SIZE]; + static final long[] rsrc2 = new long[SIZE]; + + static { + for (int i = 0; i < SIZE; i++) { + src1[i] = 0x1_0000_0001L; + src2[i] = 0x2_0000_0002L; + mask_arr[i] = (i % 2) == 0; + } + MASK = VectorMask.fromArray(SPECIES, mask_arr, 0); + + RD.fill(RD.longs(), rsrc1); + RD.fill(RD.longs(), rsrc2); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } + + // Case 1: Negative mask (-2L = 0xFFFF_FFFF_FFFF_FFFE). + @Test + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testNegativeMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, -2L).lanewise(MUL, v2.lanewise(AND, -2L)).intoArray(res, 0); + } + + @Run(test = "testNegativeMask") + public void runNegativeMask() { + testNegativeMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] & -2L) * (src2[i] & -2L); + } + Verify.checkEQ(res, expected); + } + + // Case 3: Mask = 0x1_0000_0000L (bit 32 set, exceeds uint range). + @Test + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, phase = CompilePhase.MATCHING, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testBit32SetMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, 0x1_0000_0000L).lanewise(MUL, v2.lanewise(AND, 0x1_0000_0000L)).intoArray(res, 0); + } + + @Run(test = "testBit32SetMask") + public void runBit32SetMask() { + testBit32SetMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] & 0x1_0000_0000L) * (src2[i] & 0x1_0000_0000L); + } + Verify.checkEQ(res, expected); + } + + // Case 4: Mask = Long.MIN_VALUE (0x8000_0000_0000_0000). + @Test + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testMinValueMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, Long.MIN_VALUE).lanewise(MUL, v2.lanewise(AND, Long.MIN_VALUE)).intoArray(res, 0); + } + + @Run(test = "testMinValueMask") + public void runMinValueMask() { + testMinValueMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] & Long.MIN_VALUE) * (src2[i] & Long.MIN_VALUE); + } + Verify.checkEQ(res, expected); + } + + // Case 5: Mask = 0xFFFF_FFFFL (exactly uint max, boundary valid case). + @Test + @IR(counts = {IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_SVE2, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_NEON, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testUintMaxMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, 0xFFFF_FFFFL).lanewise(MUL, v2.lanewise(AND, 0xFFFF_FFFFL)).intoArray(res, 0); + } + + @Run(test = "testUintMaxMask") + public void runUintMaxMask() { + testUintMaxMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] & 0xFFFF_FFFFL) * (src2[i] & 0xFFFF_FFFFL); + } + Verify.checkEQ(res, expected); + } + + // Case 6: Small mask (0xFFFFL), clearly fits in uint. + @Test + @IR(counts = {IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_SVE2, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_NEON, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testSmallMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, 0xFFFFL).lanewise(MUL, v2.lanewise(AND, 0xFFFFL)).intoArray(res, 0); + } + + @Run(test = "testSmallMask") + public void runSmallMask() { + testSmallMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] & 0xFFFFL) * (src2[i] & 0xFFFFL); + } + Verify.checkEQ(res, expected); + } + + // Case 7: URShift by 32 clears upper doubleword. + @Test + @IR(counts = {IRNode.MUL_VL, " >0 ", + IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_SVE2, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_NEON, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testURShift32() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(LSHR, 32).lanewise(MUL, v2.lanewise(LSHR, 32)).intoArray(res, 0); + } + + @Run(test = "testURShift32") + public void runURShift32() { + testURShift32(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] >>> 32) * (src2[i] >>> 32); + } + Verify.checkEQ(res, expected); + } + + // Case 8: Asymmetric — one input valid uint mask, other negative mask. + @Test + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testAsymmetricMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, 0xFFFF_FFFFL).lanewise(MUL, v2.lanewise(AND, -2L)).intoArray(res, 0); + } + + @Run(test = "testAsymmetricMask") + public void runAsymmetricMask() { + testAsymmetricMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] & 0xFFFF_FFFFL) * (src2[i] & -2L); + } + Verify.checkEQ(res, expected); + } + + // Case 9: Mixed — one input URShift (valid), other negative mask (invalid). + // Note: -2L is used (not -1L) since AND with -1L is identity and gets folded. + @Test + @IR(counts = {IRNode.URSHIFT_VL, " >0 ", + IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testMixedURShiftAndNegMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(LSHR, 32).lanewise(MUL, v2.lanewise(AND, -2L)).intoArray(res, 0); + } + + @Run(test = "testMixedURShiftAndNegMask") + public void runMixedURShiftAndNegMask() { + testMixedURShiftAndNegMask(); + long[] expected = new long[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++) { + expected[i] = (src1[i] >>> 32) * (src2[i] & -2L); + } + Verify.checkEQ(res, expected); + } + + // Case 10: Predicated AndV (uint path). Inactive lanes preserves destination with non-zero upper 32 bits. + @Test + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx512f", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testPredicatedAndMask() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(AND, 0xFFFF_FFFFL, MASK).lanewise(MUL, v2.lanewise(AND, 0xFFFF_FFFFL, MASK)).intoArray(res, 0); + } + + @Run(test = "testPredicatedAndMask") + public void runPredicatedAndMask() { + testPredicatedAndMask(); + long[] expected = new long[SIZE]; + for (int i = 0; i < SIZE; i++) { + long a = mask_arr[i] ? (src1[i] & 0xFFFF_FFFFL) : src1[i]; + long b = mask_arr[i] ? (src2[i] & 0xFFFF_FFFFL) : src2[i]; + expected[i] = a * b; + } + Verify.checkEQ(res, expected); + } + + // Case 11: Predicated URShiftVL by 32 (uint path). Inactive lanes preserves destination with non-zero upper 32 bits. + @Test + @IR(counts = {IRNode.URSHIFT_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx512f", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testPredicatedURShift32() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(LSHR, 32, MASK).lanewise(MUL, v2.lanewise(LSHR, 32, MASK)).intoArray(res, 0); + } + + @Run(test = "testPredicatedURShift32") + public void runPredicatedURShift32() { + testPredicatedURShift32(); + long[] expected = new long[SIZE]; + for (int i = 0; i < SIZE; i++) { + long a = mask_arr[i] ? (src1[i] >>> 32) : src1[i]; + long b = mask_arr[i] ? (src2[i] >>> 32) : src2[i]; + expected[i] = a * b; + } + Verify.checkEQ(res, expected); + } + + // Case 12: Predicated RShiftVL (arithmetic) by 32. + @Test + @IR(counts = {IRNode.RSHIFT_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true", "rvv", "true"}) + @IR(failOn = {IRNode.X86_VMULDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx512f", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_INT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_INT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void testPredicatedRShift32() { + LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); + v1.lanewise(ASHR, 32, MASK).lanewise(MUL, v2.lanewise(ASHR, 32, MASK)).intoArray(res, 0); + } + + @Run(test = "testPredicatedRShift32") + public void runPredicatedRShift32() { + testPredicatedRShift32(); + long[] expected = new long[SIZE]; + for (int i = 0; i < SIZE; i++) { + long a = mask_arr[i] ? (src1[i] >> 32) : src1[i]; + long b = mask_arr[i] ? (src2[i] >> 32) : src2[i]; + expected[i] = a * b; + } + Verify.checkEQ(res, expected); + } + + // Random-constant correctness cases with no IR rules. + + // Case 13: AND pattern with random masks on both inputs. + @Test + public static void testRandomAndMasks() { + LongVector v1 = LongVector.fromArray(SPECIES, rsrc1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, rsrc2, 0); + v1.lanewise(AND, RAND_MASK1).lanewise(MUL, v2.lanewise(AND, RAND_MASK2)).intoArray(res, 0); + } + + @Run(test = "testRandomAndMasks") + public void runRandomAndMasks() { + testRandomAndMasks(); + long[] expected = new long[SIZE]; + for (int i = 0; i < SIZE; i++) { + expected[i] = (rsrc1[i] & RAND_MASK1) * (rsrc2[i] & RAND_MASK2); + } + Verify.checkEQ(res, expected); + } + + // Case 14: URShiftV pattern with a random shift count on both inputs. + @Test + public static void testRandomURShift() { + LongVector v1 = LongVector.fromArray(SPECIES, rsrc1, 0); + LongVector v2 = LongVector.fromArray(SPECIES, rsrc2, 0); + v1.lanewise(LSHR, RAND_SHIFT1).lanewise(MUL, v2.lanewise(LSHR, RAND_SHIFT1)).intoArray(res, 0); + } + + @Run(test = "testRandomURShift") + public void runRandomURShift() { + testRandomURShift(); + long[] expected = new long[SIZE]; + for (int i = 0; i < SIZE; i++) { + expected[i] = (rsrc1[i] >>> RAND_SHIFT1) * (rsrc2[i] >>> RAND_SHIFT1); + } + Verify.checkEQ(res, expected); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorReassociations.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorReassociations.java index c6a11627215..1fc1c6ff799 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorReassociations.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorReassociations.java @@ -23,7 +23,8 @@ /* * @test - * @bug 8358521 + * @bug 8358521 8385833 + * @key randomness * @summary Test reassociation of broadcasted inputs across vector operations * @modules jdk.incubator.vector * @library /test/lib / @@ -32,7 +33,10 @@ package compiler.vectorapi; +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; import compiler.lib.ir_framework.*; +import compiler.lib.verify.*; import jdk.incubator.vector.*; import java.util.stream.IntStream; @@ -602,4 +606,56 @@ public class TestVectorReassociations { ByteVector.broadcast(BSP, bb)) .intoArray(byteOut, 0); } + + private static final Generators RD = Generators.G; + + static int uA, uB, uC; + + static { + Generator ig = RD.ints(); + uA = ig.next(); uB = ig.next(); uC = ig.next(); + } + + static int[] umaxIntOut = new int[ISP.length()]; + static int[] uminIntOut = new int[ISP.length()]; + + // UMAX(UMAX(bcast(uA), bcast(uB)), bcast(uC)). + @Test + @IR(counts = { IRNode.UMAX_VI, " >0 " }, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) + @Warmup(value = 10000) + static void test_int_umax_all_broadcast() { + IntVector.broadcast(ISP, uA) + .lanewise(VectorOperators.UMAX, uB) + .lanewise(VectorOperators.UMAX, uC) + .intoArray(umaxIntOut, 0); + } + + @Check(test = "test_int_umax_all_broadcast") + static void check_int_umax_all_broadcast() { + int e = VectorMath.maxUnsigned(VectorMath.maxUnsigned(uA, uB), uC); + for (int v : umaxIntOut) { + Verify.checkEQ(v, e); + } + } + + // UMIN(UMIN(bcast(uA), bcast(uB)), bcast(uC)). + @Test + @IR(counts = { IRNode.UMIN_VI, " >0 " }, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) + @Warmup(value = 10000) + static void test_int_umin_all_broadcast() { + IntVector.broadcast(ISP, uA) + .lanewise(VectorOperators.UMIN, uB) + .lanewise(VectorOperators.UMIN, uC) + .intoArray(uminIntOut, 0); + } + + @Check(test = "test_int_umin_all_broadcast") + static void check_int_umin_all_broadcast() { + int e = VectorMath.minUnsigned(VectorMath.minUnsigned(uA, uB), uC); + for (int v : uminIntOut) { + Verify.checkEQ(v, e); + } + } } diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorBitwiseBlendTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorBitwiseBlendTest.java new file mode 100644 index 00000000000..feeb79254b4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorBitwiseBlendTest.java @@ -0,0 +1,217 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8382052 + * @key randomness + * @library /test/lib / + * @summary IR tests for Vector BITWISE_BLEND optimization + * @modules jdk.incubator.vector + * + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import compiler.lib.generators.*; +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; + +public class VectorBitwiseBlendTest { + + private static final Generators RD = Generators.G; + + private static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + private static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + private static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + private static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + + private static final int BUF_LEN = 256; + + private static final byte[] ba = new byte[BUF_LEN]; + private static final byte[] bb = new byte[BUF_LEN]; + private static final byte[] bc = new byte[BUF_LEN]; + private static final byte[] br = new byte[BUF_LEN]; + + private static final short[] sa = new short[BUF_LEN]; + private static final short[] sb = new short[BUF_LEN]; + private static final short[] sc = new short[BUF_LEN]; + private static final short[] sr = new short[BUF_LEN]; + + private static final int[] ia = new int[BUF_LEN]; + private static final int[] ib = new int[BUF_LEN]; + private static final int[] ic = new int[BUF_LEN]; + private static final int[] ir = new int[BUF_LEN]; + + private static final long[] la = new long[BUF_LEN]; + private static final long[] lb = new long[BUF_LEN]; + private static final long[] lc = new long[BUF_LEN]; + private static final long[] lr = new long[BUF_LEN]; + + private static final boolean[] mask_arr = new boolean[BUF_LEN]; + + static { + Generator iGen = RD.ints(); + Generator lGen = RD.longs(); + + for (int i = 0; i < BUF_LEN; i++) { + mask_arr[i] = (i & 1) != 0; + ba[i] = iGen.next().byteValue(); + bb[i] = iGen.next().byteValue(); + bc[i] = iGen.next().byteValue(); + sa[i] = iGen.next().shortValue(); + sb[i] = iGen.next().shortValue(); + sc[i] = iGen.next().shortValue(); + } + RD.fill(iGen, ia); + RD.fill(iGen, ib); + RD.fill(iGen, ic); + RD.fill(lGen, la); + RD.fill(lGen, lb); + RD.fill(lGen, lc); + } + + @Test + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testUnmaskedBlendByte() { + ByteVector va = ByteVector.fromArray(B_SPECIES, ba, 0); + ByteVector vb = ByteVector.fromArray(B_SPECIES, bb, 0); + ByteVector vc = ByteVector.fromArray(B_SPECIES, bc, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc).intoArray(br, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testUnmaskedBlendShort() { + ShortVector va = ShortVector.fromArray(S_SPECIES, sa, 0); + ShortVector vb = ShortVector.fromArray(S_SPECIES, sb, 0); + ShortVector vc = ShortVector.fromArray(S_SPECIES, sc, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc).intoArray(sr, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testUnmaskedBlendInt() { + IntVector va = IntVector.fromArray(I_SPECIES, ia, 0); + IntVector vb = IntVector.fromArray(I_SPECIES, ib, 0); + IntVector vc = IntVector.fromArray(I_SPECIES, ic, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc).intoArray(ir, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testUnmaskedBlendLong() { + LongVector va = LongVector.fromArray(L_SPECIES, la, 0); + LongVector vb = LongVector.fromArray(L_SPECIES, lb, 0); + LongVector vc = LongVector.fromArray(L_SPECIES, lc, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc).intoArray(lr, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BLEND_B, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BLEND_B, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testMaskedBlendByte() { + VectorMask mask = VectorMask.fromArray(B_SPECIES, mask_arr, 0); + ByteVector va = ByteVector.fromArray(B_SPECIES, ba, 0); + ByteVector vb = ByteVector.fromArray(B_SPECIES, bb, 0); + ByteVector vc = ByteVector.fromArray(B_SPECIES, bc, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc, mask).intoArray(br, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BLEND_S, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BLEND_S, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testMaskedBlendShort() { + VectorMask mask = VectorMask.fromArray(S_SPECIES, mask_arr, 0); + ShortVector va = ShortVector.fromArray(S_SPECIES, sa, 0); + ShortVector vb = ShortVector.fromArray(S_SPECIES, sb, 0); + ShortVector vc = ShortVector.fromArray(S_SPECIES, sc, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc, mask).intoArray(sr, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BLEND_I, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BLEND_I, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testMaskedBlendInt() { + VectorMask mask = VectorMask.fromArray(I_SPECIES, mask_arr, 0); + IntVector va = IntVector.fromArray(I_SPECIES, ia, 0); + IntVector vb = IntVector.fromArray(I_SPECIES, ib, 0); + IntVector vc = IntVector.fromArray(I_SPECIES, ic, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc, mask).intoArray(ir, 0); + } + + @Test + @IR(counts = { IRNode.VECTOR_BLEND_L, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve2", "false" }, + applyIf = { "MaxVectorSize", "<= 16" }) + @IR(counts = { IRNode.VECTOR_BLEND_L, "= 1", + IRNode.VECTOR_BITWISE_BLEND, "= 1" }, + applyIfCPUFeature = { "sve2", "true" }) + public static void testMaskedBlendLong() { + VectorMask mask = VectorMask.fromArray(L_SPECIES, mask_arr, 0); + LongVector va = LongVector.fromArray(L_SPECIES, la, 0); + LongVector vb = LongVector.fromArray(L_SPECIES, lb, 0); + LongVector vc = LongVector.fromArray(L_SPECIES, lc, 0); + va.lanewise(VectorOperators.BITWISE_BLEND, vb, vc, mask).intoArray(lr, 0); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java index cb5b95109f5..3413ece592f 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java @@ -66,6 +66,8 @@ import compiler.lib.template_framework.library.Expression.Nesting; import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.TestFrameworkClass; import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.ShortCarriesFloat16Type; +import compiler.lib.template_framework.library.VectorElementType; import compiler.lib.template_framework.library.VectorType; /** @@ -162,23 +164,39 @@ public class VectorExpressionFuzzer { // - We check correctness with a reference method that does the same but runs in the interpreter. // - Input values are delivered via fields or array loads. // - The final vector is written into an array, and that array is returned. - var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> scope( - let("elementType", ((VectorType.Vector)expression.returnType).elementType), - """ - try { - #elementType[] out = new #elementType[1000]; - """, - expression.asToken(arguments), ".intoArray(out, 0);\n", - "return out;\n", - expression.info.exceptions.stream().map(exception -> - "} catch (" + exception + " e) { return e;\n" - ).toList(), - """ - } finally { - // Just javac is happy if there are no exceptions to catch. - } - """ - )); + // + // NaN canonicalization (Float16Vector only): the {@code short} carrier of Float16Vector lanes + // distinguishes multiple NaN bit patterns, so a structural comparison between two distinct NaN + // bit patterns would spuriously fail. We widen the {@code short[]} carrier to {@code float[]} + // via {@link Float#float16ToFloat}, which returns a canonical NaN for any NaN input. + var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> { + VectorType.Vector retType = (VectorType.Vector) expression.returnType; + boolean float16Result = retType.elementType instanceof ShortCarriesFloat16Type; + return scope( + let("carrierType", retType.elementType.carrierTypeName()), + """ + try { + #carrierType[] out = new #carrierType[1000]; + """, + expression.asToken(arguments), ".intoArray(out, 0);\n", + float16Result + ? """ + // Float16Vector NaN canonicalization: widen short carrier to float for compare. + float[] outF = new float[out.length]; + for (int i = 0; i < out.length; i++) { outF[i] = Float.float16ToFloat(out[i]); } + return outF; + """ + : "return out;\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just javac is happy if there are no exceptions to catch. + } + """ + ); + }); var template2 = Template.make("type", (VectorType.Vector type) -> { // The depth determines roughly how many operations are going to be used in the expression. @@ -210,24 +228,25 @@ public class VectorExpressionFuzzer { )); } default -> { - if (argumentType instanceof PrimitiveType t) { + if (argumentType instanceof VectorElementType vet) { // We can use the LibraryRGN to create a new value for the primitive in each // invocation. We have to make sure to call the LibraryRNG in the "defineAndFill", // so we get the same value for both test and reference. If we called LibraryRNG // for "use", we would get separate values, which is not helpful. arguments.add(new TestArgument( - List.of(t.name(), " ", name, " = ", t.callLibraryRNG(), ";\n"), + List.of(vet.carrierTypeName(), " ", name, " = ", vet.callLibraryRNG(), ";\n"), name, - List.of(t.name(), " ", name), + List.of(vet.carrierTypeName(), " ", name), name )); } else if (argumentType instanceof VectorType.Vector t) { - PrimitiveType et = t.elementType; + VectorElementType et = t.elementType; + String fillMethod = (et instanceof ShortCarriesFloat16Type) ? "fill_float16" : "fill"; arguments.add(new TestArgument( - List.of(et.name(), "[] ", name, " = new ", et.name(), "[1000];\n", - "LibraryRNG.fill(", name,");\n"), + List.of(et.carrierTypeName(), "[] ", name, " = new ", et.carrierTypeName(), "[1000];\n", + "LibraryRNG.", fillMethod, "(", name,");\n"), name, - List.of(et.name(), "[] ", name), + List.of(et.carrierTypeName(), "[] ", name), List.of(t.name(), ".fromArray(", t.speciesName, ", ", name, ", 0)") )); } else if (argumentType instanceof VectorType.Mask t) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java index a8394f41f8a..4d8344e729e 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.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 @@ -31,9 +31,9 @@ import java.lang.reflect.Array; /** * @test - * @bug 8341137 + * @bug 8341137 8383905 * @key randomness - * @summary Optimize long vector multiplication using x86 VPMUL[U]DQ instruction. + * @summary Optimize long vector multiplication. * @modules jdk.incubator.vector * @library /test/lib / * @run driver compiler.vectorapi.VectorMultiplyOpt @@ -80,7 +80,7 @@ public class VectorMultiplyOpt { public static void main(String[] args) { TestFramework testFramework = new TestFramework(); - testFramework.setDefaultWarmup(5000) + testFramework.setDefaultWarmup(10000) .addFlags("--add-modules=jdk.incubator.vector") .start(); System.out.println("PASSED"); @@ -107,9 +107,15 @@ public class VectorMultiplyOpt { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern1() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -130,9 +136,15 @@ public class VectorMultiplyOpt { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern2() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -153,9 +165,15 @@ public class VectorMultiplyOpt { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern3() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -176,9 +194,15 @@ public class VectorMultiplyOpt { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern4() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -199,9 +223,15 @@ public class VectorMultiplyOpt { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) - @Warmup(value = 10000) + @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_int_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern5() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -225,9 +255,15 @@ public class VectorMultiplyOpt { @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.RSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.RSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) - @Warmup(value = 10000) + @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_int_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern6() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -247,4 +283,63 @@ public class VectorMultiplyOpt { validate("pattern6 ", res, lsrc1, lsrc2, (l1, l2) -> (l1 >> shift5) * (l2 >> shift5)); } + // Same-operand multiplication (v * v) where v has zero-extended high bits. + // On NEON this should map to the dedicated rule that emits a single xtn. + @Test + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) + @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon_same", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void test_pattern7() { + int i = 0; + for (; i < LSP.loopBound(res.length); i += LSP.length()) { + LongVector vsrc = LongVector.fromArray(LSP, lsrc1, i) + .lanewise(VectorOperators.AND, mask1); + vsrc.lanewise(VectorOperators.MUL, vsrc).intoArray(res, i); + } + for (; i < res.length; i++) { + long x = lsrc1[i] & mask1; + res[i] = x * x; + } + } + + @Check(test = "test_pattern7") + public void test_pattern7_validate() { + validate("pattern7 ", res, lsrc1, lsrc1, (l1, l2) -> { long x = l1 & mask1; return x * x; }); + } + + // Same-operand multiplication (v * v) where v has sign-extended high bits. + // On NEON this should map to the dedicated rule that emits a single xtn. + @Test + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) + @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) + @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_int_neon_same", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void test_pattern8() { + int i = 0; + for (; i < LSP.loopBound(res.length); i += LSP.length()) { + LongVector vsrc = IntVector.fromArray(ISP, isrc1, i) + .convert(VectorOperators.I2L, 0) + .reinterpretAsLongs(); + vsrc.lanewise(VectorOperators.MUL, vsrc).intoArray(res, i); + } + for (; i < res.length; i++) { + res[i] = Math.multiplyFull(isrc1[i], isrc1[i]); + } + } + + @Check(test = "test_pattern8") + public void test_pattern8_validate() { + validate("pattern8 ", res, isrc1, isrc1, (i1, i2) -> Math.multiplyFull((int)i1, (int)i1)); + } } diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java index 79cde2f0d26..4c448564a87 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java @@ -29,7 +29,7 @@ * @key randomness * @summary Test vectorization of rotate byte and short * @library /test/lib / - * @run main/othervm TestRotateByteAndShortVector + * @run driver ${test.main.class} */ import java.util.Random; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java index 78dd4f50a06..e5a6966cdcf 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java @@ -31,7 +31,7 @@ * @library /test/lib / * @modules java.base/jdk.internal.math * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*" - * @run main compiler.vectorization.TestRoundVectorDoubleRandom + * @run driver ${test.main.class} */ package compiler.vectorization; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java index 474601346e8..92b6d3b9840 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java @@ -31,7 +31,7 @@ * @library /test/lib / * @modules java.base/jdk.internal.math * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*" - * @run main compiler.vectorization.TestRoundVectorFloatRandom + * @run driver ${test.main.class} */ package compiler.vectorization; diff --git a/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java index c06473d26c5..404412e5fc7 100644 --- a/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java +++ b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java @@ -541,21 +541,34 @@ public class VectorAlgorithmsImpl { int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W var vresult = IntVector.zero(SPECIES_I); + final boolean isLE = java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN; int i; for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { var vb = ByteVector.fromArray(SPECIES_B, a, i); // Add 128 to each byte. var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) .reinterpretAsShorts(); - // Each short lane contains 2 bytes, crunch them. - var vi = vs.and((short)0xff) // lower byte - .mul((short)31) - .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte - .reinterpretAsInts(); - // Each int contains 2 shorts, crunch them. - var v = vi.and(0xffff) // lower short - .mul(31 * 31) - .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Each short lane contains 2 bytes. + // Extract them in logical byte order (b0, b1), independent of platform endianness. + ShortVector firstByte = isLE ? vs.and((short)0xff) // b0 + : vs.lanewise(VectorOperators.LSHR, 8); // b0 on BE + ShortVector secondByte = isLE ? vs.lanewise(VectorOperators.LSHR, 8) // b1 + : vs.and((short)0xff); // b1 on BE + // Combine each byte pair into a pairwise hash value. + var vi = firstByte.mul((short)31) + .add(secondByte) + .reinterpretAsInts(); + // Each int lane contains two pairwise hash chunks: + // p0 = b0 * 31 + b1 + // p1 = b2 * 31 + b3 + // Extract them in logical order, independent of platform endianness. + IntVector firstPair = isLE ? vi.and(0xffff) // p0 + : vi.lanewise(VectorOperators.LSHR, 16); // p0 on BE + IntVector secondPair = isLE ? vi.lanewise(VectorOperators.LSHR, 16) // p1 + : vi.and(0xffff); // p1 on BE + // Crunch the pairwise results into one value. + var v = firstPair.mul(31 * 31) + .add(secondPair); // Add the correction for the 128 additions above. v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); // Every element of v now contains a crunched int-package of 4 bytes. diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java index 48b2ff754ad..f1140533d25 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java @@ -24,18 +24,10 @@ /* * @test * @summary Vectorization test on array copy + * @requires vm.compiler2.enabled * @library /test/lib / * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayCopyTest - * - * @requires vm.compiler2.enabled + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java index 8d0ba2be589..3708fc87f29 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on array index fill * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayIndexFillTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java index b7044b1c79d..90e4955bee3 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. 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 @@ -25,24 +26,11 @@ * @test * @summary Vectorization test on array invariant fill * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:-OptimizeFill - * compiler.vectorization.runner.ArrayInvariantFillTest - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:+OptimizeFill - * compiler.vectorization.runner.ArrayInvariantFillTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} NoOptimizeFill + * @run driver ${test.main.class} OptimizeFill */ package compiler.vectorization.runner; @@ -68,11 +56,22 @@ public class ArrayInvariantFillTest extends VectorizationTestRunner { doubleInv = ran.nextDouble(); } + // We must pass the flags directly to the Test VM, and not the Driver VM in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return switch (args[0]) { + case "NoOptimizeFill" -> new String[]{"-XX:-OptimizeFill"}; + case "OptimizeFill" -> new String[]{"-XX:+OptimizeFill"}; + default -> throw new RuntimeException("Test argument not recognized: " + args[0]); + }; + } + // ---------------- Simple Fill ---------------- @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, - applyIf = {"OptimizeFill", "false"}, - counts = {IRNode.REPLICATE_B, ">0"}) + // TODO 8387402 + //@IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, + // applyIf = {"OptimizeFill", "false"}, + // counts = {IRNode.REPLICATE_B, ">0"}) @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, applyIf = {"OptimizeFill", "true"}, counts = {IRNode.REPLICATE_B, "0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java index e2d28cbf083..2699afda5cc 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,18 +28,10 @@ * @bug 8183390 8332905 * @summary Vectorization test on bug-prone shift operation * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayShiftOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java index f9c5f6199f1..d6f2febb06f 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java @@ -27,33 +27,12 @@ * @bug 8183390 8340010 8342095 * @summary Vectorization test on array type conversions * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * * @requires vm.compiler2.enabled * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_yAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_yAV + * @run driver ${test.main.class} nCOH_nAV + * @run driver ${test.main.class} nCOH_yAV + * @run driver ${test.main.class} yCOH_nAV + * @run driver ${test.main.class} yCOH_yAV */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java index 8b4513b8490..f6874a03ffb 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on array unsafe operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayUnsafeOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java index ba82013e182..3a61b365800 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -27,17 +27,9 @@ * @summary Vectorization test on basic boolean operations * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicBooleanOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java index a336b32f7b9..acbf44c471c 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,19 +26,9 @@ * @test * @summary Vectorization test on basic byte operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:CompileCommand=CompileOnly,compiler.vectorization.runner.BasicByteOpTest::* - * -XX:LoopUnrollLimit=1000 - * compiler.vectorization.runner.BasicByteOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; @@ -64,6 +54,12 @@ public class BasicByteOpTest extends VectorizationTestRunner { } } + // We must pass the flags directly to the test-VM, and not the driver vm in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return new String[]{"-XX:CompileCommand=CompileOnly,compiler.vectorization.runner.BasicByteOpTest::*", "-XX:LoopUnrollLimit=1000"}; + } + // ---------------- Arithmetic ---------------- @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java index 4211d5eec5e..be462f0be16 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on basic char operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicCharOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java index 8d5925ec8c6..1adb89591a5 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,18 +27,10 @@ * @test * @summary Vectorization test on basic double operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicDoubleOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java index b89d068d8af..870b8746baf 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java @@ -25,18 +25,10 @@ * @test * @summary Vectorization test on basic float operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicFloatOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java index e096f7878ab..8849418e609 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on basic int operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicIntOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java index a6767054958..5404d943bbc 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on basic long operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicLongOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java index b957a00278a..4c7221dea52 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java @@ -27,17 +27,9 @@ * @bug 8183390 8342095 * @summary Vectorization test on basic short operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicShortOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java index c8a3c71bdee..27058012f36 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,26 +26,13 @@ * @test * @summary Vectorization test on loop array index computation * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest nAV_ySAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest yAV_ySAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest nAV_nSAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest yAV_nSAC - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} nAV_ySAC + * @run driver ${test.main.class} yAV_ySAC + * @run driver ${test.main.class} nAV_nSAC + * @run driver ${test.main.class} yAV_nSAC */ package compiler.vectorization.runner; @@ -60,10 +47,10 @@ public class LoopArrayIndexComputeTest extends VectorizationTestRunner { @Override protected String[] testVMFlags(String[] args) { return switch (args[0]) { - case "nAV_ySAC" -> new String[]{"-XX:-AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; - case "yAV_ySAC" -> new String[]{"-XX:+AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; - case "nAV_nSAC" -> new String[]{"-XX:-AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; - case "yAV_nSAC" -> new String[]{"-XX:+AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; + case "nAV_ySAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:-AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; + case "yAV_ySAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:+AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; + case "nAV_nSAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:-AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; + case "yAV_nSAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:+AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); } }; } diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java index c46b2e11612..714de5b3c6b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,33 +26,12 @@ * @test * @summary Vectorization test on combined operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * * @requires vm.compiler2.enabled * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest nCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest nCOH_yAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest yCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest yCOH_yAV + * @run driver ${test.main.class} nCOH_nAV + * @run driver ${test.main.class} nCOH_yAV + * @run driver ${test.main.class} yCOH_nAV + * @run driver ${test.main.class} yCOH_yAV */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java index e36e4097813..51326956983 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on simple control flow in loop * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopControlFlowTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java index 06a3eb33bc3..cad2af04a9b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on loops with live out nodes * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopLiveOutNodesTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java index 2db565461ac..a36d11198e7 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on different loop ranges and strides * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopRangeStrideTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java index 546d99f5cce..9b9dcb03f6e 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java @@ -25,19 +25,10 @@ * @test * @summary Vectorization test on reduction operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopReductionOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java index 4dbfba02a43..4be74d20733 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * 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 @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on multiple loops in a method * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.MultipleLoopsTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java index dbc999647ad..347571fc95b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. 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 @@ -25,18 +26,9 @@ * @test * @summary Vectorization test with small strip mining iterations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:LoopStripMiningIter=10 - * compiler.vectorization.runner.StripMinedLoopTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; @@ -59,6 +51,12 @@ public class StripMinedLoopTest extends VectorizationTestRunner { } } + // We must pass the flags directly to the Test VM, and not the Driver VM in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return new String[]{"-XX:LoopStripMiningIter=10"}; + } + @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.STORE_VECTOR, ">0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java index 7f8e4ec3b39..7e836d78849 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. 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 @@ -29,14 +30,23 @@ import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import jdk.test.lib.Utils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.ProcessTools; import jdk.test.whitebox.WhiteBox; public class VectorizationTestRunner { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final String VERIFY_CORRECTNESS_ARG = "--verify-vectorization-correctness"; + + private static class Flags { + private static final WhiteBox WHITEBOX = WhiteBox.getWhiteBox(); + } private static final int COMP_LEVEL_INTP = 0; private static final int COMP_LEVEL_C2 = 4; @@ -52,6 +62,35 @@ public class VectorizationTestRunner { // invokes it twice - first time in the interpreter and second time compiled // by C2. Then this runner compares the two return values. Hence we require // each test method returning a primitive value or an array of primitive type. + runCorrectnessTestsInTestVM(args); + + // 2) Vectorization ability test + // To test vectorizability, invoke the IR test framework to check existence of + // expected C2 IR node. + TestFramework irTest = new TestFramework(klass); + irTest.addFlags(testVMFlags(args)); + irTest.start(); + } + + private void runCorrectnessTestsInTestVM(String[] args) { + List command = new ArrayList<>(); + command.addAll(Arrays.asList(testVMFlags(args))); + command.add("-Xbootclasspath/a:."); + command.add("-XX:+UnlockDiagnosticVMOptions"); + command.add("-XX:+WhiteBoxAPI"); + command.add(getClass().getName()); + command.add(VERIFY_CORRECTNESS_ARG); + command.add(getClass().getName()); + try { + ClassFileInstaller.main("jdk.test.whitebox.WhiteBox"); + ProcessTools.executeTestJava(command).shouldHaveExitValue(0); + } catch (Exception e) { + throw new RuntimeException("Vectorization correctness test failed", e); + } + } + + private void runCorrectnessTests() { + Class klass = getClass(); for (Method method : klass.getDeclaredMethods()) { try { if (method.isAnnotationPresent(Test.class)) { @@ -63,13 +102,6 @@ public class VectorizationTestRunner { "." + method.getName() + ": " + e.getMessage()); } } - - // 2) Vectorization ability test - // To test vectorizability, invoke the IR test framework to check existence of - // expected C2 IR node. - TestFramework irTest = new TestFramework(klass); - irTest.addFlags(testVMFlags(args)); - irTest.start(); } // Override this to add extra flags. @@ -109,22 +141,25 @@ public class VectorizationTestRunner { Object expected = null; Object actual = null; - // Temporarily disable the compiler and invoke the method to get reference - // result from the interpreter - WB.setBooleanVMFlag("UseCompiler", false); + // Temporarily make the test method not compilable and invoke it to get the + // reference result from the interpreter. + Flags.WHITEBOX.makeMethodNotCompilable(method, CompLevel.ANY.getValue(), true); + Flags.WHITEBOX.makeMethodNotCompilable(method, CompLevel.ANY.getValue(), false); try { expected = method.invoke(this); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); } catch (Exception e) { e.printStackTrace(); fail("Exception is thrown in test method invocation (interpreter)."); + } finally { + // Make the test method compilable again + Flags.WHITEBOX.clearMethodState(method); } - assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); - WB.setBooleanVMFlag("UseCompiler", true); // Compile the method and invoke it again long enqueueTime = System.currentTimeMillis(); - WB.enqueueMethodForCompilation(method, COMP_LEVEL_C2); - while (WB.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { + Flags.WHITEBOX.enqueueMethodForCompilation(method, COMP_LEVEL_C2); + while (Flags.WHITEBOX.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { Thread.sleep(100 /*ms*/); } try { @@ -133,7 +168,7 @@ public class VectorizationTestRunner { e.printStackTrace(); fail("Exception is thrown in test method invocation (C2)."); } - assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_C2); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_C2); // Check if two invocations return the same Class retType = method.getReturnType(); @@ -172,11 +207,10 @@ public class VectorizationTestRunner { } private static VectorizationTestRunner createTestInstance(String testName) { - if (!testName.toLowerCase().endsWith(".java")) { - fail("Invalid test file name " + testName); + if (testName.toLowerCase().endsWith(".java")) { + testName = testName.substring(0, testName.length() - 5); + testName = testName.replace('/', '.'); } - testName = testName.substring(0, testName.length() - 5); - testName = testName.replace('/', '.'); VectorizationTestRunner instance = null; try { @@ -196,7 +230,13 @@ public class VectorizationTestRunner { } public static void main(String[] args) { - VectorizationTestRunner testObj = createTestInstance(Utils.TEST_NAME); + VectorizationTestRunner testObj; + if (args.length > 0 && args[0].equals(VERIFY_CORRECTNESS_ARG)) { + testObj = createTestInstance(args[1]); + testObj.runCorrectnessTests(); + return; + } + testObj = createTestInstance(Utils.TEST_NAME); testObj.run(args); } } diff --git a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java index 446cf3c27bb..bcd6e33c81e 100644 --- a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java +++ b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java @@ -71,6 +71,11 @@ * * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocHumongousFragment + * + * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot * TestAllocHumongousFragment * @@ -78,6 +83,11 @@ * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahAllocFailureALot * TestAllocHumongousFragment + * + * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocHumongousFragment */ /* diff --git a/test/hotspot/jtreg/gc/TestCodeCacheUnload.java b/test/hotspot/jtreg/gc/TestCodeCacheUnload.java new file mode 100644 index 00000000000..03c857dba1e --- /dev/null +++ b/test/hotspot/jtreg/gc/TestCodeCacheUnload.java @@ -0,0 +1,177 @@ +/* + * 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 gc; + +/* + * @test id=serial + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Serial + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseSerialGC gc.TestCodeCacheUnload + */ + +/* + * @test id=parallel + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Parallel + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseParallelGC gc.TestCodeCacheUnload + */ + +/* + * @test id=g1 + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.G1 + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseG1GC gc.TestCodeCacheUnload + */ + +/* + * @test id=shenandoah + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Shenandoah + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseShenandoahGC gc.TestCodeCacheUnload + */ + +/* + * @test id=z + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Z + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseZGC gc.TestCodeCacheUnload + */ + +import java.lang.reflect.Method; + +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + +public class TestCodeCacheUnload { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public static class Target { + public static int test(int value) { + return value + 1; + } + } + + private static void compileAndMakeNotEntrant() throws Exception { + Method method = Target.class.getDeclaredMethod("test", int.class); + + method.invoke(null, 1); + if (!WB.enqueueMethodForCompilation(method, 1 /* compLevel */)) { + throw new AssertionError("Failed to enqueue target for compilation"); + } + while (WB.isMethodQueuedForCompilation(method)) { + Thread.sleep(50); + } + if (!WB.isMethodCompiled(method)) { + throw new AssertionError("Target is not compiled"); + } + + int deoptimized = WB.deoptimizeMethod(method); + if (deoptimized == 0) { + throw new AssertionError("No target nmethod was made not-entrant"); + } + } + + private static int countNotEntrantEntries() { + OutputAnalyzer output = new JMXExecutor().execute("Compiler.codelist"); + String target = "gc.TestCodeCacheUnload$Target.test"; + int result = 0; + + for (String line : output.asLines()) { + if (!line.contains(target)) { + continue; + } + + System.out.println("Found codelist entry: " + line); + String[] parts = line.trim().split("\\s+"); + int codeState = Integer.parseInt(parts[2]); + if (codeState == 1 /* not_entrant */) { + result++; + } + } + + return result; + } + + public static void main(String[] args) throws Exception { + compileAndMakeNotEntrant(); + WB.fullGC(); + + int notEntrantEntries = countNotEntrantEntries(); + System.out.println("Target not-entrant entries after 1 full GC: " + notEntrantEntries); + if (notEntrantEntries != 0) { + throw new AssertionError("Expected one full GC to unload the not-entrant nmethod"); + } + } +} diff --git a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java index 014181b2fce..6be05def108 100644 --- a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.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 @@ -167,13 +167,13 @@ public class TestNewRatioFlag { long maxOld = HeapRegionUsageTool.getOldUsage().getMax(); int regionSize = wb.g1RegionSize(); - int youngListLength = (int) ((initEden + initSurv) / regionSize); + int numYoungRegions = (int) ((initEden + initSurv) / regionSize); int maxRegions = (int) (maxOld / regionSize); - int expectedYoungListLength = (int) (maxRegions / (double) (expectedRatio + 1)); + int expectedNumYoungRegions = (int) (maxRegions / (double) (expectedRatio + 1)); - if (youngListLength != expectedYoungListLength) { - throw new RuntimeException("Expected G1 young list length is: " + expectedYoungListLength - + ", but observed young list length is: " + youngListLength); + if (numYoungRegions != expectedNumYoungRegions) { + throw new RuntimeException("Expected G1 number of young regions is: " + expectedNumYoungRegions + + ", but observed number of young regions is: " + numYoungRegions); } } } diff --git a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java index bdf8186bfaa..36b85e8c09d 100644 --- a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.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 @@ -116,7 +116,7 @@ public class TestSurvivorRatioFlag { * Depending on selected young GC we verify that: * - for DefNew and ParNew: eden_size / survivor_size is close to expectedRatio; * - for PSNew: survivor_size equal to young_gen_size / expectedRatio; - * - for G1: survivor_regions <= young_list_length / expectedRatio. + * - for G1: survivor_regions <= num_young_regions / expectedRatio. */ public static Void verifySurvivorRatio(int expectedRatio) { GCTypes.YoungGCType type = GCTypes.YoungGCType.getYoungGCType(); @@ -166,8 +166,8 @@ public class TestSurvivorRatioFlag { MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage(); int regionSize = wb.g1RegionSize(); - int youngListLength = (int) Math.max(NEW_SIZE / regionSize, 1); - int expectedSurvivorRegions = (int) Math.ceil(youngListLength / (double) expectedRatio); + int numYoungRegions = (int) Math.max(NEW_SIZE / regionSize, 1); + int expectedSurvivorRegions = (int) Math.ceil(numYoungRegions / (double) expectedRatio); int observedSurvivorRegions = (int) (survivorUsage.getCommitted() / regionSize); if (expectedSurvivorRegions < observedSurvivorRegions) { diff --git a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java index 34a221b1025..ca34707bf12 100644 --- a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java @@ -310,8 +310,8 @@ public class TestTargetSurvivorRatioFlag { */ public static long getMaxSurvivorSize() { if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) { - int youngLength = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1); - return (long) Math.ceil(youngLength / (double) SURVIVOR_RATIO) * wb.g1RegionSize(); + int numYoungRegions = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1); + return (long) Math.ceil(numYoungRegions / (double) SURVIVOR_RATIO) * wb.g1RegionSize(); } else { return HeapRegionUsageTool.getSurvivorUsage().getMax(); } diff --git a/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java new file mode 100644 index 00000000000..0c2d473b021 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java @@ -0,0 +1,115 @@ +/* + * 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 gc.g1; + +/* + * @test TestCodeCacheUnloadDuringConcurrentMark + * @summary Tests that G1 concurrent marking unloads a freshly not-entrant nmethod. + * @requires vm.gc.G1 + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.ClassUnloadingWithConcurrentMark != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseG1GC + * -XX:+ClassUnloadingWithConcurrentMark + * gc.g1.TestCodeCacheUnloadDuringConcurrentMark + */ + +import java.lang.reflect.Method; + +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.whitebox.WhiteBox; + +public class TestCodeCacheUnloadDuringConcurrentMark { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + static class Target { + public static int test(int value) { + return value + 1; + } + } + + private static void compileAndMakeNotEntrant(Method method) throws Exception { + Target.test(1); + if (!WB.enqueueMethodForCompilation(method, 1 /* compLevel */)) { + throw new AssertionError("Failed to enqueue target for compilation"); + } + while (WB.isMethodQueuedForCompilation(method)) { + Thread.sleep(50); + } + if (!WB.isMethodCompiled(method)) { + throw new AssertionError("Target is not compiled"); + } + + int deoptimized = WB.deoptimizeMethod(method); + if (deoptimized == 0) { + throw new AssertionError("No target nmethod was made not-entrant"); + } + } + + private static int countNotEntrantEntries() { + String target = TestCodeCacheUnloadDuringConcurrentMark.class.getName() + "$Target.test"; + int result = 0; + + for (String line : new JMXExecutor().execute("Compiler.codelist", true).asLines()) { + if (!line.contains(target)) { + continue; + } + + System.out.println("Found codelist entry: " + line); + String[] parts = line.trim().split("\\s+"); + int codeState = Integer.parseInt(parts[2]); + if (codeState == 1 /* not_entrant */) { + result++; + } + } + + return result; + } + + public static void main(String[] args) throws Exception { + compileAndMakeNotEntrant(Target.class.getDeclaredMethod("test", int.class)); + + int notEntrantEntries = countNotEntrantEntries(); + System.out.println("Target not-entrant entries before concurrent mark: " + notEntrantEntries); + if (notEntrantEntries == 0) { + throw new AssertionError("Expected a not-entrant target nmethod before concurrent mark"); + } + + WB.g1RunConcurrentGC(); + + notEntrantEntries = countNotEntrantEntries(); + System.out.println("Target not-entrant entries after concurrent mark: " + notEntrantEntries); + if (notEntrantEntries != 0) { + throw new AssertionError("Expected concurrent mark to unload the not-entrant target nmethod"); + } + } +} diff --git a/test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java b/test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java new file mode 100644 index 00000000000..c47dafc3203 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java @@ -0,0 +1,216 @@ +/* + * 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 gc.g1; + +/* + * @test TestCodeCacheWhiteBox.java + * @bug 8225186 + * @summary Test to make sure that code cache unloading does not make the VM hang when receiving + * a request while WhiteBox is holding control. + * We do that by triggering a code cache gc request (by triggering compilations) during a + * synchronous compilation while whitebox is holding control, and additionally verify that + * after the concurrent cycle additional code cache gc requests start more concurrent cycles. + * @requires vm.gc.G1 + * @requires vm.flagless + * @library /test/lib /testlibrary / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xmx20M -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. gc.g1.TestCodeCacheWhiteBox + */ + + +import java.lang.reflect.Field; + +import java.net.URL; +import java.net.URLClassLoader; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestCodeCacheWhiteBox { + public static final String AFTER_FIRST_CYCLE_MARKER = "Marker for this test"; + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static OutputAnalyzer runTest(String concPhase) throws Exception { + OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC", + "-Xmx20M", + "-XX:+UnlockDiagnosticVMOptions", + "-Xbootclasspath/a:.", + "-Xbatch", // Needed to make compilation synchronous + "-Xlog:gc=trace,codecache", + "-XX:+WhiteBoxAPI", + "-XX:ReservedCodeCacheSize=" + (Platform.is32bit() ? "4M" : "8M"), + "-XX:StartAggressiveSweepingAt=50", + "-XX:CompileCommand=compileonly,gc.g1.SomeClass::*", + "-XX:CompileCommand=compileonly,gc.g1.Foo*::*", + TestCodeCacheWhiteBoxRunner.class.getName(), + concPhase); + return output; + } + + private static void runAndCheckTest(String test) throws Exception { + OutputAnalyzer output; + + output = runTest(test); + output.shouldHaveExitValue(0); + output.shouldNotContain("ERROR"); + System.out.println(output.getStdout()); + + String[] parts = output.getStdout().split(AFTER_FIRST_CYCLE_MARKER); + + // Either "Threshold" or "Aggressive" CodeCache GC are fine for the test. + final String codecacheGCStart = "Pause Young (Concurrent Start) (CodeCache GC "; + + boolean success = parts.length == 2 && parts[1].indexOf(codecacheGCStart) != -1; + Asserts.assertTrue(success, "Could not find a CodeCache GC Threshold GC after finishing the concurrent cycle"); + } + + public static void main(String[] args) throws Exception { + runAndCheckTest(WB.BEFORE_MARKING_COMPLETED); // This one should always complete. Just for sanity checking. + runAndCheckTest(WB.G1_BEFORE_REBUILD_COMPLETED); + runAndCheckTest(WB.G1_BEFORE_CLEANUP_COMPLETED); + } +} + +class TestCodeCacheWhiteBoxRunner { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void refClass(Class clazz) throws Exception { + Field name = clazz.getDeclaredField("NAME"); + name.setAccessible(true); + name.get(null); + } + + private static class MyClassLoader extends URLClassLoader { + public MyClassLoader(URL url) { + super(new URL[]{url}, null); + } + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + try { + return super.loadClass(name, resolve); + } catch (ClassNotFoundException e) { + return Class.forName(name, resolve, TestCodeCacheWhiteBoxRunner.class.getClassLoader()); + } + } + } + + private static void triggerCodeCacheGC() { + URL url = TestCodeCacheWhiteBoxRunner.class.getProtectionDomain().getCodeSource().getLocation(); + try { + int i = 0; + do { + ClassLoader cl = new MyClassLoader(url); + refClass(cl.loadClass("gc.g1.SomeClass")); + + if (i % 20 == 0) { + System.out.println("Compiled " + i + " classes"); + } + i++; + } while (i < 200); + System.out.println("Compilation done, compiled " + i + " classes"); + } catch (Throwable t) { + System.out.println("ERROR: threw exception " + t); + } + } + + public static void main(String[] args) throws Exception { + System.out.println("Running to breakpoint: " + args[0]); + try { + WB.concurrentGCAcquireControl(); + WB.concurrentGCRunTo(args[0]); + + System.out.println("Try to trigger code cache GC"); + + Thread toRun = new Thread(() -> + { + System.out.println("Thread is running"); + triggerCodeCacheGC(); + System.out.println("Thread completed"); + }); + toRun.setDaemon(true); // non-daemon thread could prevent VM shutdown after the main thread times out + toRun.start(); + toRun.join(60_000); + + if (toRun.isAlive()) { + toRun.interrupt(); + throw new RuntimeException("ERROR: thread took too long, deadlocked?"); + } + + WB.concurrentGCRunToIdle(); + } catch (InterruptedException e) { + System.out.println("ERROR: starting helper thread"); + throw e; + } finally { + // Make sure that the marker we use to find the expected log message is printed + // before we release whitebox control, i.e. before the expected garbage collection + // can start. + System.out.println(TestCodeCacheWhiteBox.AFTER_FIRST_CYCLE_MARKER); + WB.concurrentGCReleaseControl(); + } + Thread.sleep(1000); + triggerCodeCacheGC(); + } +} + +abstract class Foo { + public abstract int foo(); +} + +class Foo1 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo2 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo3 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo4 extends Foo { + private int a; + public int foo() { return a; } +} + +class SomeClass { + static final String NAME = "name"; + + static { + int res =0; + Foo[] foos = new Foo[] { new Foo1(), new Foo2(), new Foo3(), new Foo4() }; + for (int i = 0; i < 100000; i++) { + res = foos[i % foos.length].foo(); + } + } +} diff --git a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java index 5637e578e8f..37902cad906 100644 --- a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java +++ b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.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 @@ -109,7 +109,10 @@ public class TestEagerReclaimHumongousRegions { "-Xmx20M", "-Xms20m", "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC", + "-XX:+VerifyDuringGC", + "-XX:+G1VerifyBitmaps", "-Xbootclasspath/a:.", "-Xlog:gc=debug,gc+humongous=debug", "-XX:+UnlockDiagnosticVMOptions", diff --git a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java index 68391893a32..16b73d4c354 100644 --- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java +++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java @@ -170,7 +170,8 @@ public class TestGCLogMessages { // Post Evacuate Cleanup 1 new LogMessageWithLevel("Post Evacuate Cleanup 1:", Level.DEBUG), - new LogMessageWithLevel("Merge Per-Thread State \\(ms\\):", Level.DEBUG), + new LogMessageWithLevel("Flush Per-Thread State \\(ms\\):", Level.DEBUG), + new LogMessageWithLevel("Update Code Roots \\(ms\\):", Level.DEBUG), new LogMessageWithLevel("LAB Waste:", Level.DEBUG), new LogMessageWithLevel("LAB Undo Waste:", Level.DEBUG), new LogMessageWithLevel("Pending Cards:", Level.DEBUG), @@ -188,7 +189,7 @@ public class TestGCLogMessages { new LogMessageWithLevel("Serial Free Collection Set:", Level.TRACE), new LogMessageWithLevel("Young Free Collection Set \\(ms\\):", Level.TRACE), new LogMessageWithLevel("Non-Young Free Collection Set \\(ms\\):", Level.TRACE), - new LogMessageWithLevel("Reset Partial Array State Manager \\(ms\\)", Level.TRACE), + new LogMessageWithLevel("Destroy Per-Thread State \\(ms\\):", Level.TRACE), // Misc Top-level new LogMessageWithLevel("Rebuild Free List:", Level.DEBUG), diff --git a/test/hotspot/jtreg/gc/g1/TestRemarkCleanupMXBeanCollectionUsage.java b/test/hotspot/jtreg/gc/g1/TestRemarkCleanupMXBeanCollectionUsage.java new file mode 100644 index 00000000000..6c46f8b1980 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestRemarkCleanupMXBeanCollectionUsage.java @@ -0,0 +1,102 @@ +/* + * 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 gc.g1; + +/* + * @test TestRemarkCleanupMXBeanCollectionUsage + * @bug 8386332 + * @summary Test that Remark and Cleanup correctly update old pool's getCollectionUsage() bean. + * @requires vm.gc.G1 + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @modules java.base/jdk.internal.misc + * java.management + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -XX:+UseG1GC -Xlog:gc -XX:G1HeapRegionSize=1m -Xms128m -Xmx128m + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * gc.g1.TestRemarkCleanupMXBeanCollectionUsage + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.lang.ref.Reference; + +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; + +public class TestRemarkCleanupMXBeanCollectionUsage { + private static WhiteBox wb = WhiteBox.getWhiteBox(); + private static final int M = 1024 * 1024; + + private static MemoryPoolMXBean findPoolMXBean(String name) throws Exception { + for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) { + if (pool.getName().equals(name)) { + return pool; + } + } + throw new RuntimeException("Pool " + name + " not found."); + } + + private static long getCollectionUsageUsedAndPrint(MemoryPoolMXBean pool, String message) { + long result = pool.getCollectionUsage().getUsed(); + System.out.println(message + ": " + result); + return result; + } + + public static void main(String[] args) throws Exception { + Object throwaway = new Object(); + try { + MemoryPoolMXBean oldPool = findPoolMXBean("G1 Old Gen"); + + wb.concurrentGCAcquireControl(); + wb.fullGC(); + long initialUsage = getCollectionUsageUsedAndPrint(oldPool, "Initial Usage"); + + // Allocate something in old gen. CollectionUsage should be updated. + throwaway = new byte[M]; // Humongous allocation. + wb.fullGC(); + long afterFirstUsage = getCollectionUsageUsedAndPrint(oldPool, "After first alloc usage"); + Asserts.assertTrue(afterFirstUsage >= initialUsage + M, + "Full GC should updated collectionUsage. Before " + afterFirstUsage + " after " + initialUsage); + + // Remark pause should update collectionUsage, i.e. the following release of the memory be noticed. + throwaway = null; + wb.concurrentGCRunTo(wb.G1_AFTER_REBUILD_STARTED); + long afterRemarkUsage = getCollectionUsageUsedAndPrint(oldPool, "After Remark usage"); + Asserts.assertTrue(afterRemarkUsage < afterFirstUsage - M, + "Remark pause should have updated getCollectionUsage(). Before " + afterFirstUsage + " after " + afterRemarkUsage); + + // Cleanup pause should not update collectionUsage, i.e. the following allocation go unnoticed. + throwaway = new byte[M]; + wb.concurrentGCRunTo(wb.G1_AFTER_CLEANUP_STARTED); + long afterCleanupUsage = getCollectionUsageUsedAndPrint(oldPool, "After Cleanup usage"); + Asserts.assertTrue(afterCleanupUsage == afterRemarkUsage, + "Cleanup pause should not update getCollectionUsage(). Before " + afterRemarkUsage + " after " + afterCleanupUsage); + } finally { + wb.concurrentGCReleaseControl(); + Reference.reachabilityFence(throwaway); + } + } +} diff --git a/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java b/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java index 5a69a6e5288..b5cf4c333a6 100644 --- a/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java +++ b/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -23,6 +23,8 @@ package gc.g1; +import java.lang.ref.Reference; + /* * @test TestVerificationInConcurrentCycle * @requires vm.gc.G1 @@ -34,6 +36,7 @@ package gc.g1; * @run main/othervm * -Xbootclasspath/a:. * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:G1HeapRegionSize=2m * -XX:+VerifyBeforeGC -XX:+VerifyDuringGC -XX:+VerifyAfterGC * -XX:+UseG1GC -XX:+G1VerifyHeapRegionCodeRoots * -XX:+G1VerifyBitmaps @@ -52,6 +55,7 @@ package gc.g1; * @run main/othervm * -Xbootclasspath/a:. * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:G1HeapRegionSize=2m * -XX:+VerifyBeforeGC -XX:+VerifyDuringGC -XX:+VerifyAfterGC * -XX:+UseG1GC -XX:+G1VerifyHeapRegionCodeRoots * gc.g1.TestVerificationInConcurrentCycle @@ -64,27 +68,50 @@ public class TestVerificationInConcurrentCycle { private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static Object[] allocateHumongous() { + Object[] result = new Object[7]; + for (int i = 0; i < result.length; i++) { + result[i] = new byte[1024 * 1024]; // Is humongous. + } + return result; + } + + private static void dropHalf(Object[] array) { + for (int i = 0; i < array.length; i++) { + if (i % 2 == 0) { + array[i] = null; + } + } + } // All testN() assume initial state is idle, and restore that state. private static void testFullGCAt(String at) throws Exception { System.out.println("testSimpleCycle"); + + Object[] objects = allocateHumongous(); try { // Run one cycle. WB.concurrentGCRunTo(at); + dropHalf(objects); WB.fullGC(); } finally { WB.concurrentGCRunToIdle(); + Reference.reachabilityFence(objects); } } private static void testYoungGCAt(String at) throws Exception { System.out.println("testSimpleCycle"); + + Object[] objects = allocateHumongous(); try { // Run one cycle. WB.concurrentGCRunTo(at); + dropHalf(objects); WB.youngGC(); } finally { WB.concurrentGCRunToIdle(); + Reference.reachabilityFence(objects); } } diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java index f650e53a25f..c4dd2900c86 100644 --- a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.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 @@ -30,9 +30,10 @@ * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xmx32m -XX:G1NumCollectionsKeepPinned=1 - -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:G1MixedGCLiveThresholdPercent=100 - -XX:G1HeapWastePercent=0 -Xlog:gc,gc+ergo+cset=trace gc.g1.pinnedobjs.TestDroppedRetainedTAMS + * -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xmx32m -XX:G1NumCollectionsKeepPinned=1 + * -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:+G1VerifyBitmaps + * -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 + * -Xlog:gc,gc+ergo+cset=trace gc.g1.pinnedobjs.TestDroppedRetainedTAMS */ package gc.g1.pinnedobjs; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java index 457af294f6f..8488b8f4a8d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java @@ -70,6 +70,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocIntArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot * TestAllocIntArrays * @@ -80,6 +85,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocIntArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestAllocIntArrays */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java index 1df8f7453f7..bc8c451450c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java @@ -70,6 +70,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot * TestAllocObjectArrays * @@ -80,6 +85,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestAllocObjectArrays */ @@ -126,6 +136,12 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:ShenandoahPinRegionRate=1000 + * -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * TestAllocObjectArrays */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java index fa6f3ab9b04..a1d06945b79 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java @@ -64,6 +64,11 @@ * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify * TestAllocObjects * + * @run main/othervm/timeout=480 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocObjects + * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot @@ -76,6 +81,11 @@ * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestAllocObjects */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java index 1b607bf96ca..e790851e2e8 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java @@ -58,6 +58,11 @@ * * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestJcmdHeapDump + * + * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestJcmdHeapDump */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java index 569406fa95c..fbf3cd5c34b 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java @@ -58,6 +58,12 @@ * * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * -Dtarget=1000 + * TestLotsOfCycles + * + * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -Dtarget=1000 * TestLotsOfCycles */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java index d25c8dd0f5e..010bdb5e4f1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java @@ -66,6 +66,11 @@ * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestRetainObjects */ @@ -106,6 +111,11 @@ * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * TestRetainObjects */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java index 79259168bf3..fa140d62a66 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java @@ -68,6 +68,11 @@ * -XX:+ShenandoahAllocFailureALot * TestSieveObjects * + * @run main/othervm/timeout=240 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestSieveObjects + * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestSieveObjects @@ -108,6 +113,11 @@ * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify * TestSieveObjects * + * @run main/othervm/timeout=480 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestSieveObjects + * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * TestSieveObjects diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java index 2af784fd034..fe3c8a5a476 100644 --- a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java @@ -41,9 +41,11 @@ import jdk.test.lib.process.OutputAnalyzer; public class TestOldGrowthTriggers { public static void makeOldAllocations() { - // Expect most of the BitSet entries placed into array to be promoted, and most will eventually become garbage within old + // Keep the majority of BitSet entries (5/8, 960) long-lived so they promote and grow old generation + // well past the old GC trigger threshold. A smaller long-lived set can fall just short and only + // intermittently trigger an old GC, so don't reduce the array size or the promoted fraction. - final int ArraySize = 1024; // 1K entries + final int ArraySize = 1536; // 1536 entries (1024 + 512) final int RefillIterations = 128; BitSet[] array = new BitSet[ArraySize]; @@ -57,8 +59,10 @@ public class TestOldGrowthTriggers { int replaceIndex = i; int deriveIndex = i-1; + // 3/8 entries are replaced each pass to trigger young gcs. + // 5/8 entries are never touched, so they age each cycle. switch (i & 0x7) { - case 0,1,2 -> { + case 0,1 -> { // creates new BitSet, releases old BitSet, // create ephemeral data while computing BitSet result = (BitSet) array[deriveIndex].clone(); @@ -67,12 +71,12 @@ public class TestOldGrowthTriggers { } array[replaceIndex] = result; } - case 3,4 -> { + case 2 -> { // creates new BitSet, releases old BitSet BitSet result = (BitSet) array[deriveIndex].clone(); array[replaceIndex] = result; } - case 5,6,7 -> { + default -> { // do nothing, let all objects in the array age to increase pressure on old generation } } @@ -110,6 +114,8 @@ public class TestOldGrowthTriggers { "-XX:ShenandoahMinOldGenGrowthRemainingHeapPercent=100", "-XX:ShenandoahGuaranteedYoungGCInterval=0", "-XX:ShenandoahGuaranteedOldGCInterval=0", + "-XX:ShenandoahGenerationalMinTenuringAge=2", + "-XX:ShenandoahGenerationalMaxTenuringAge=2", "-XX:-UseCompactObjectHeaders" ); @@ -127,6 +133,8 @@ public class TestOldGrowthTriggers { "-XX:ShenandoahMinOldGenGrowthRemainingHeapPercent=100", "-XX:ShenandoahGuaranteedYoungGCInterval=0", "-XX:ShenandoahGuaranteedOldGCInterval=0", + "-XX:ShenandoahGenerationalMinTenuringAge=2", + "-XX:ShenandoahGenerationalMaxTenuringAge=2", "-XX:+UseCompactObjectHeaders" ); } diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 3bf0e59dce3..532bf6c07de 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -66,6 +66,11 @@ import java.io.IOException; * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + * + * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ @@ -144,6 +149,12 @@ import java.io.IOException; * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline + * -XX:ShenandoahPinRegionRate=1000 + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + * + * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java index 5418bb12492..9b2eb530b2a 100644 --- a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java @@ -71,6 +71,11 @@ package gc.stress.gcold; * * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * gc.stress.gcold.TestGCOld 50 1 20 10 10000 + * + * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * gc.stress.gcold.TestGCOld 50 1 20 10 10000 */ diff --git a/test/hotspot/jtreg/gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java b/test/hotspot/jtreg/gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java index 743e59bca61..3c49f94428b 100644 --- a/test/hotspot/jtreg/gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java @@ -30,7 +30,7 @@ package jdk.jfr.event.gc.detailed; * @requires vm.hasJFR * @requires vm.gc.Shenandoah * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseShenandoahGC -Xmx64m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithShenandoah + * @run main/othervm -XX:+UseShenandoahGC -Xmx128m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithShenandoah */ /** @@ -39,7 +39,7 @@ package jdk.jfr.event.gc.detailed; * @requires vm.hasJFR * @requires vm.gc.Shenandoah * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx64m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithShenandoah + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx128m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithShenandoah */ public class TestStressAllocationGCEventsWithShenandoah { diff --git a/test/hotspot/jtreg/gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java b/test/hotspot/jtreg/gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java index cb7e4b5d1ee..94fdc83bffc 100644 --- a/test/hotspot/jtreg/gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java @@ -30,7 +30,7 @@ package jdk.jfr.event.gc.detailed; * @requires vm.hasJFR * @requires vm.gc.Shenandoah * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseShenandoahGC -Xmx256m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithShenandoah 1048576 + * @run main/othervm -XX:+UseShenandoahGC -Xmx512m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithShenandoah 1048576 */ /** @@ -39,7 +39,7 @@ package jdk.jfr.event.gc.detailed; * @requires vm.hasJFR * @requires vm.gc.Shenandoah * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx256m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithShenandoah 1048576 + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx512m -XX:ActiveProcessorCount=1 jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithShenandoah 1048576 */ public class TestStressBigAllocationGCEventsWithShenandoah { diff --git a/test/hotspot/jtreg/gtest/AssemblerGtests.java b/test/hotspot/jtreg/gtest/AssemblerGtests.java new file mode 100644 index 00000000000..19fb3398267 --- /dev/null +++ b/test/hotspot/jtreg/gtest/AssemblerGtests.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. All rights reserved. + * + * 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. + * + */ + +/* + * This runs the MacroAssembler gtests related to Klass de- and encoding + * (for now, only on aarch64) with and without COH. + */ + +/* @test id=coh + * @summary Run Assembler-related gtests + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @requires vm.flagless + * @requires os.arch=="aarch64" + * @run main/native GTestWrapper --gtest_filter=AssemblerAArch64::decode_encode_klass* -XX:+UseCompactObjectHeaders + */ + +/* @test id=noncoh + * @summary Run Assembler-related gtests + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @requires vm.flagless + * @requires os.arch=="aarch64" + * @run main/native GTestWrapper --gtest_filter=AssemblerAArch64::decode_encode_klass* -XX:-UseCompactObjectHeaders + */ + diff --git a/test/hotspot/jtreg/gtest/ShenandoahGtests.java b/test/hotspot/jtreg/gtest/ShenandoahGtests.java new file mode 100644 index 00000000000..1e8c404fc12 --- /dev/null +++ b/test/hotspot/jtreg/gtest/ShenandoahGtests.java @@ -0,0 +1,31 @@ +/* + * 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. + * + */ + +/* @test + * @summary Run Shenandoah gtests + * @library /test/lib + * @requires vm.gc.Shenandoah + * @requires vm.debug + * @run main/native GTestWrapper --gtest_filter=Shenandoah* + */ diff --git a/test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java b/test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java index 9e3ae8de832..f58a287a895 100644 --- a/test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java +++ b/test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, 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 @@ -71,7 +71,7 @@ public class PreviewVersion { pb = ProcessTools.createLimitedTestJavaProcessBuilder("--enable-preview", "-Xlog:class+preview", "-cp", "." + File.pathSeparator + System.getProperty("test.classes"), "PVTest"); oa = new OutputAnalyzer(pb.start()); - oa.shouldContain("[info][class,preview] Loading class PVTest that depends on preview features"); + oa.shouldMatch("\\[info *\\]\\[class,preview *\\] Loading class PVTest that depends on preview features"); oa.shouldHaveExitValue(0); // Subtract 1 from class's major version. The class should fail to load diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java index 99c2d27f8d3..8c530936065 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java +++ b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.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 @@ -58,6 +58,7 @@ public class VMDeprecatedOptions { // { , } // deprecated non-alias flags: {"AllowRedefinitionToAddDeleteMethods", "true"}, + {"CompilationMode", "default"}, // deprecated alias flags (see also aliased_jvm_flags): {"CreateMinidumpOnCrash", "false"} diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java index d14dbc93245..f4ef0800a73 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java @@ -54,7 +54,7 @@ public class CompressedClassPointersEncodingScheme { "-XX:" + (COH ? "+" : "-") + "UseObjectMonitorTable", "-XX:CompressedClassSpaceBaseAddress=" + forceAddress, "-XX:CompressedClassSpaceSize=" + classSpaceSize, - "-Xmx128m", + "-Xmx64m", "-Xlog:metaspace*", "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); @@ -71,35 +71,6 @@ public class CompressedClassPointersEncodingScheme { output.shouldContain("Narrow klass base: " + expectedEncodingBaseString + ", Narrow klass shift: " + expectedEncodingShift); } - private static void testFailure(String forceAddressString) throws IOException { - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-Xshare:off", // to make CompressedClassSpaceBaseAddress work - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:-UseCompactObjectHeaders", - "-XX:CompressedClassSpaceBaseAddress=" + forceAddressString, - "-Xmx128m", - "-Xlog:metaspace*", - "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - output.reportDiagnosticSummary(); - - // We ignore cases where we were not able to map at the force address - if (!output.contains("Successfully forced class space address to " + forceAddressString)) { - throw new SkippedException("Skipping because we cannot force ccs to " + forceAddressString); - } - - if (Platform.isAArch64()) { - output.shouldHaveExitValue(1); - output.shouldContain("Error occurred during initialization of VM"); - output.shouldContain("CompressedClassSpaceBaseAddress=" + forceAddressString + - " given with shift 0, cannot be used to encode class pointers"); - } else { - output.shouldHaveExitValue(0); - } - } - final static long K = 1024; final static long M = K * 1024; final static long G = M * 1024; @@ -108,53 +79,47 @@ public class CompressedClassPointersEncodingScheme { // Expecting base=0, shift=0 test(4 * G - 128 * M, false, 128 * M, 0, 0); - // Test ccs nestling right at the end of the 32G range - // Expecting: - // - non-aarch64: base=0, shift=3 - // - aarch64: base to start of class range, shift 0 - if (Platform.isAArch64()) { - // The best we can do on aarch64 is to be *near* the end of the 32g range, since a valid encoding base - // on aarch64 must be 4G aligned, and the max. class space size is 3G. - long forceAddress = 0x7_0000_0000L; // 28g, and also a valid EOR immediate - test(forceAddress, false, 3 * G, forceAddress, 0); - } else { - test(32 * G - 128 * M, false, 128 * M, 0, 3); - } + // aarch64 does not do extended zero based encoding (shift>0) + boolean expectExtendedZeroBasedEncoding = !Platform.isAArch64(); - // Test ccs starting *below* 4G, but extending upwards beyond 4G. All platforms except aarch64 should pick - // zero based encoding. On aarch64, this test is excluded since the only valid mode would be XOR, but bit - // pattern for base and bit pattern would overlap. - if (!Platform.isAArch64()) { - test(4 * G - 128 * M, false, 2 * 128 * M, 0, 3); - } - // add more... + // Test ccs nestling right at the end of the 32G range. + // Expect all platforms but aarch64 to do shift-extended zero-based encoding; + long forceAddress = 32 * G - 128 * M; + test(forceAddress, false, 128 * M, + expectExtendedZeroBasedEncoding ? 0 : forceAddress, // expected base + expectExtendedZeroBasedEncoding ? 3 : 0 // expected shift + ); + + // Test ccs starting *below* 4G, but extending upwards beyond 4G. + // Expect all platforms but aarch64 to do shift-extended zero-based encoding; aarch64 does not do that but + // drops right to non-zero-based with shift = 0 + forceAddress = 4 * G - 128 * M; + test(forceAddress, false, 2 * 128 * M, + expectExtendedZeroBasedEncoding ? 0 : forceAddress, // expected base + expectExtendedZeroBasedEncoding ? 3 : 0 // expected shift + ); // Compact Object Header Mode: - // On aarch64 and x64 we expect the VM to chose the smallest possible shift value needed to cover - // the encoding range. We expect the encoding Base to start at the class space start - but to enforce that, - // we choose a high address. - if (Platform.isAArch64() || Platform.isX64() || Platform.isRISCV64()) { - long forceAddress = 32 * G; + // We expect the VM to chose the smallest possible shift value needed to cover the encoding range. + // We expect the encoding Base to start at the class space start - but to enforce that, + // we choose unsuited to even shift-extended zero-based mode. + forceAddress = 32 * G; - long ccsSize = 128 * M; - int expectedShift = 6; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); + test(forceAddress, true, 128 * M, forceAddress, 6); + test(forceAddress, true, 256 * M, forceAddress, 7); + test(forceAddress, true, 512 * M, forceAddress, 8); + test(forceAddress, true, G, forceAddress, 9); + test(forceAddress, true, 3 * G, forceAddress, 10); - ccsSize = 512 * M; - expectedShift = 8; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - - ccsSize = G; - expectedShift = 9; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - - ccsSize = 3 * G; - expectedShift = 10; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - } - - // Test failure for -XX:CompressedClassBaseAddress and -Xshare:off - testFailure("0x0000040001000000"); + // Test a "crooked" base address: + // - just aligned enough to pass metaspace reserve alignment test of 16MB. + // - not encodable on aarch64 as logical immediate + // - sufficiently complex enough to need multiple moves on risc platforms to materialize as immediate + // - small enough to not cause test errors on small devices (e.g. arm64 39bit address space) + // - large enough to not end up with zero-based encoding + forceAddress = 0x0000000d55000000L; + test(forceAddress, true, 32 * M, forceAddress, 6); + test(forceAddress, false, 32 * M, forceAddress, 0); } } diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java index 61d017d2264..4e177a6fe1d 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java @@ -126,7 +126,7 @@ public class AccessZeroNKlassHitsProtectionZone { private static void run_test(boolean COH, boolean CDS) throws IOException, SkippedException { // Notes: - // We want to enforce zero-based encoding, to test the protection page in that case. For zero-based encoding, + // We want to enforce non-zero-based encoding, to test the protection page in that case. For zero-based encoding, // protection page is at address zero, no need to test that. // If CDS is on, we never use zero-based, forceBase is ignored. // If CDS is off, we use forceBase to (somewhat) reliably force the encoding base to beyond 32G, diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java new file mode 100644 index 00000000000..59692d94d2f --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.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 + * @summary Test that YMM and ZMM registers are correctly dumped in hs_err for different UseAVX settings + * @library /test/lib + * @requires os.family == "linux" & os.arch == "amd64" + * @requires vm.cpu.features ~= ".*avx.*" + * @requires vm.debug == true + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver TestAVXRegisterDump + */ + +// Note: this test can only run on debug since it relies on VMError::controlled_crash() which +// only exists in debug builds. + +import java.io.File; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestAVXRegisterDump { + + public static void main(String[] args) throws Exception { + + if (args.length > 0 && args[0].equals("crash")) { + WhiteBox.getWhiteBox().controlledCrash(2); + throw new RuntimeException("Still alive?"); + } + + // Test UseAVX=1 (XMM only) + testWithUseAVX(1); + + // Test UseAVX=2 (YMM) + testWithUseAVX(2); + + // Test UseAVX=3 (ZMM + K masks if available) + testWithUseAVX(3); + } + + static void testWithUseAVX(int useAVX) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:UseAVX=" + useAVX, + "-XX:-CreateCoredumpOnCrash", + "-Xmx100M", + TestAVXRegisterDump.class.getName(), "crash"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); + + File hsErrFile = HsErrFileUtils.openHsErrFileFromOutput(output); + validateRegisterContent(hsErrFile, useAVX); + } + + static Pattern[] createRegisterPatterns(String regType, int count) { + Pattern[] patterns = new Pattern[count]; + for (int i = 0; i < count; i++) { + // Create regex pattern to match entire register line (e.g., "XMM[0]=0xHEX 0xHEX") + // Used with Matcher.matches() which requires matching the entire line + patterns[i] = Pattern.compile(regType + "\\[" + i + "\\]=.*"); + } + return patterns; + } + + static void validateRegisterContent(File hsErrFile, int useAVX) throws Exception { + if (useAVX == 1) { + validateRegistersUseAVX1(hsErrFile); + } else if (useAVX == 2) { + validateRegistersUseAVX2(hsErrFile); + } else if (useAVX == 3) { + validateRegistersUseAVX3(hsErrFile); + } + } + + static void validateRegistersUseAVX1(File hsErrFile) throws Exception { + // UseAVX=1: XMM registers only (0-15) + Pattern[] positivePatterns = createRegisterPatterns("XMM", 16); + Pattern[] negativePatterns = new Pattern[] { + Pattern.compile("YMM\\[.*\\]=.*"), + Pattern.compile("ZMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, negativePatterns, false, false); + } + + static void validateRegistersUseAVX2(File hsErrFile) throws Exception { + // UseAVX=2: YMM registers only (0-15) + Pattern[] positivePatterns = createRegisterPatterns("YMM", 16); + Pattern[] negativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + Pattern.compile("ZMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, negativePatterns, false, false); + } + + static void validateRegistersUseAVX3(File hsErrFile) throws Exception { + // UseAVX=3: ZMM + K masks (if available) or fallback to YMM + // Try ZMM first, then fallback to YMM if CPU doesn't support AVX-512 + try { + Pattern[] zmmPatterns = createRegisterPatterns("ZMM", 32); + Pattern[] zmmNegativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, zmmPatterns, zmmNegativePatterns, false, false); + + Pattern[] kPatterns = createRegisterPatterns("K", 8); + HsErrFileUtils.checkHsErrFileContent(hsErrFile, kPatterns, null, false, false); + } catch (RuntimeException e) { + // If ZMM not found, try YMM + Pattern[] ymmPatterns = createRegisterPatterns("YMM", 16); + Pattern[] ymmNegativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, ymmPatterns, ymmNegativePatterns, false, false); + } + } +} diff --git a/test/hotspot/jtreg/runtime/NMT/MallocLimitTest.java b/test/hotspot/jtreg/runtime/NMT/MallocLimitTest.java index 8b711304a99..14832e833e1 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocLimitTest.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocLimitTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022 SAP SE. All rights reserved. - * 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 @@ -106,7 +106,7 @@ public class MallocLimitTest { ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=1m"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotHaveExitValue(0); - output.shouldContain("[nmt] MallocLimit: total limit: 1024K (fatal)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: total limit: 1024K \\(fatal\\)"); output.shouldMatch("# fatal error: MallocLimit: reached global limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1024K\\)"); } @@ -114,8 +114,8 @@ public class MallocLimitTest { ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=1m:oom"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotHaveExitValue(0); - output.shouldContain("[nmt] MallocLimit: total limit: 1024K (oom)"); - output.shouldMatch(".*\\[warning\\]\\[nmt\\] MallocLimit: reached global limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1024K\\)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: total limit: 1024K \\(oom\\)"); + output.shouldMatch("\\[warning\\]\\[nmt *\\] MallocLimit: reached global limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1024K\\)"); // The rest is fuzzy. We may get SIGSEGV or a native OOM message, depending on how the failing allocation was handled. } @@ -123,7 +123,7 @@ public class MallocLimitTest { ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=compiler:1234k", "-Xcomp"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotHaveExitValue(0); - output.shouldContain("[nmt] MallocLimit: category \"mtCompiler\" limit: 1234K (fatal)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: category \"mtCompiler\" limit: 1234K \\(fatal\\)"); output.shouldMatch("# fatal error: MallocLimit: reached category \"mtCompiler\" limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1234K\\)"); } @@ -131,8 +131,8 @@ public class MallocLimitTest { ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=compiler:1234k:oom", "-Xcomp"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotHaveExitValue(0); - output.shouldContain("[nmt] MallocLimit: category \"mtCompiler\" limit: 1234K (oom)"); - output.shouldMatch(".*\\[warning\\]\\[nmt\\] MallocLimit: reached category \"mtCompiler\" limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1234K\\)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: category \"mtCompiler\" limit: 1234K \\(oom\\)"); + output.shouldMatch("\\[warning\\]\\[nmt *\\] MallocLimit: reached category \"mtCompiler\" limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1234K\\)"); // The rest is fuzzy. We may get SIGSEGV or a native OOM message, depending on how the failing allocation was handled. } @@ -140,9 +140,9 @@ public class MallocLimitTest { ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=other:2g,compiler:1g:oom,internal:1k"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotHaveExitValue(0); - output.shouldContain("[nmt] MallocLimit: category \"mtCompiler\" limit: 1024M (oom)"); - output.shouldContain("[nmt] MallocLimit: category \"mtInternal\" limit: 1024B (fatal)"); - output.shouldContain("[nmt] MallocLimit: category \"mtOther\" limit: 2048M (fatal)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: category \"mtCompiler\" limit: 1024M \\(oom\\)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: category \"mtInternal\" limit: 1024B \\(fatal\\)"); + output.shouldMatch("\\[nmt *\\] MallocLimit: category \"mtOther\" limit: 2048M \\(fatal\\)"); output.shouldMatch("# fatal error: MallocLimit: reached category \"mtInternal\" limit \\(triggering allocation size: \\d+[BKM], allocated so far: \\d+[BKM], limit: 1024B\\)"); } diff --git a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java index f1d4964d6cf..f9c0d8a1dc3 100644 --- a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java +++ b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, 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 @@ -34,6 +34,7 @@ */ import jdk.test.lib.Utils; +import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.whitebox.WhiteBox; @@ -70,7 +71,12 @@ public class NMTPrintMallocSiteOfCorruptedMemory { case HEADER_AND_SITE_ARG, FOOTER_AND_SITE_ARG -> output.shouldContain("allocation-site cannot be shown since the marker is also corrupted."); case HEADER_ARG, FOOTER_ARG -> { output.shouldContain("allocated from:"); - output.shouldMatch("\\[.*\\]WB_NMTMalloc\\+0x.*"); + // We will only have this if NMT can determine the name of the symbols in the stack trace. + // This will most likely be true if the platform is Linux and it's a debug build, + // so we only check it for that platform and build. + if (Platform.isLinux() && Platform.isDebugBuild()) { + output.shouldMatch("\\[.*\\]WB_NMTMalloc\\+0x.*"); + } } } } diff --git a/test/hotspot/jtreg/runtime/StackGuardPages/TestStackGuardPagesNative.java b/test/hotspot/jtreg/runtime/StackGuardPages/TestStackGuardPagesNative.java index d3a132f4b62..7d3aae549e1 100644 --- a/test/hotspot/jtreg/runtime/StackGuardPages/TestStackGuardPagesNative.java +++ b/test/hotspot/jtreg/runtime/StackGuardPages/TestStackGuardPagesNative.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ * @modules java.base/jdk.internal.misc * @library /test/lib * @requires os.family == "linux" + * @requires !jdk.static * @compile DoOverflow.java * @run main/native TestStackGuardPagesNative */ diff --git a/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c b/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c index 4608627688c..d19cd26a75c 100644 --- a/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c +++ b/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c @@ -51,7 +51,7 @@ JavaVM* _jvm; -static jmp_buf context; +static sigjmp_buf context; static volatile int _last_si_code = -1; static volatile int _failures = 0; diff --git a/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java b/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java index 7226c8ed058..b939aceee95 100644 --- a/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java +++ b/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java @@ -21,38 +21,91 @@ * questions. */ -/** - * @test TestSpinPause - * @summary JVM runtime can use SpinPause function for synchronized statements. - * Check different implementations of JVM SpinPause don't crash JVM. +/* + * @test id=default + * @summary Check the default SpinPause implementation for synchronized statements. * @bug 8278241 * @library /test/lib - * * @requires os.arch=="aarch64" - * * @run main/othervm TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xint TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xcomp TestSpinPause + */ + +/* + * @test id=none + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=none. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + */ + +/* + * @test id=nop + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=nop. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + */ + +/* + * @test id=isb + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=isb. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + */ + +/* + * @test id=yield + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=yield. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + */ + +/* + * @test id=nop-count-10 + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=nop and OnSpinWaitInstCount=10. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + */ + +/* + * @test id=isb-count-3 + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=isb and OnSpinWaitInstCount=3. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + */ + +/* + * @test id=yield-count-3 + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=yield and OnSpinWaitInstCount=3. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause */ diff --git a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java index 8241b0f9a2e..2c796243cbf 100644 --- a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java +++ b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java @@ -62,7 +62,7 @@ public class TestCDSVMCrash { throw new Error("Expected VM to crash"); } catch(RuntimeException e) { if (!e.getMessage().contains("A fatal error has been detected")) { - throw new Error("Expected message: A fatal error has been detected"); + throw new Error("Expected message: A fatal error has been detected. Instead message is: " + e.getMessage()); } } System.out.println("PASSED"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/LambdaProxyClasslist.java b/test/hotspot/jtreg/runtime/cds/appcds/LambdaProxyClasslist.java index adafa0d9c69..db431b180eb 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/LambdaProxyClasslist.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/LambdaProxyClasslist.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 @@ -59,7 +59,7 @@ public class LambdaProxyClasslist { out = TestCommon.dump(appJar, TestCommon.list("LambHello", "@lambda-proxy LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()Z")); - out.shouldContain("[warning][cds] No invoke dynamic constant pool entry can be found for class LambHello. The classlist is probably out-of-date.") + out.shouldMatch("\\[warning\\]\\[cds *\\] No invoke dynamic constant pool entry can be found for class LambHello\\. The classlist is probably out-of-date\\.") .shouldHaveExitValue(0); // 4. More blank spaces in between items should be fine. diff --git a/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithOldClass.java b/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithOldClass.java index d7ce22e3cf1..796437c0474 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithOldClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithOldClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, 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 @@ -67,7 +67,7 @@ public class LambdaWithOldClass { .setUseVersion(false) .addSuffix(mainClass); OutputAnalyzer output = CDSTestUtils.runWithArchive(runOpts); - output.shouldContain("[class,load] LambdaWithOldClassApp source: shared objects file") + output.shouldMatch("\\[class,load *\\] LambdaWithOldClassApp source: shared objects file") .shouldHaveExitValue(0); if (!CDSTestUtils.isAOTClassLinkingEnabled()) { // With AOTClassLinking, we don't archive any lambda with old classes in the method diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ProhibitedPackage.java b/test/hotspot/jtreg/runtime/cds/appcds/ProhibitedPackage.java index ea69d2db3cd..be5175e6d6a 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/ProhibitedPackage.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/ProhibitedPackage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -65,7 +65,7 @@ public class ProhibitedPackage { TestCommon.list("java/lang/Prohibited", "ProhibitedHelper"), "-Xlog:class+load") .shouldContain("Dumping") - .shouldNotContain("[info][class,load] java.lang.Prohibited source: ") + .shouldNotMatch("\\[info *\\]\\[class,load *\\] java.lang.Prohibited source: ") .shouldHaveExitValue(0); // Try loading the class in a prohibited package with various -Xshare diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java index 4cc6ef81c45..864ce88cd11 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.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 @@ -58,7 +58,7 @@ public class AOTLoggingTag { "-cp", appJar, helloClass); out = CDSTestUtils.executeAndLog(pb, "train"); - out.shouldContain("[aot] Writing binary AOTConfiguration file:"); + out.shouldMatch("\\[aot *\\] Writing binary AOTConfiguration file:"); out.shouldHaveExitValue(0); //---------------------------------------------------------------------- @@ -70,7 +70,7 @@ public class AOTLoggingTag { "-Xlog:aot", "-cp", appJar); out = CDSTestUtils.executeAndLog(pb, "asm"); - out.shouldContain("[aot] Opened AOT configuration file hello.aotconfig"); + out.shouldMatch("\\[aot *\\] Opened AOT configuration file hello\\.aotconfig"); out.shouldHaveExitValue(0); //---------------------------------------------------------------------- @@ -80,7 +80,7 @@ public class AOTLoggingTag { "-Xlog:aot", "-cp", appJar, helloClass); out = CDSTestUtils.executeAndLog(pb, "prod"); - out.shouldContain("[aot] Opened AOT cache hello.aot"); + out.shouldMatch("\\[aot *\\] Opened AOT cache hello\\.aot"); out.shouldHaveExitValue(0); //---------------------------------------------------------------------- @@ -90,7 +90,7 @@ public class AOTLoggingTag { "-XX:AOTMode=on", "-cp", appJar, helloClass); out = CDSTestUtils.executeAndLog(pb, "prod"); - out.shouldContain("[aot] An error has occurred while processing the AOT cache. Run with -Xlog:aot for details."); + out.shouldMatch("\\[aot *\\] An error has occurred while processing the AOT cache\\. Run with -Xlog:aot for details\\."); out.shouldNotHaveExitValue(0); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java index 8107e3fe0a3..82a5ccb2142 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.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,6 +24,7 @@ /* * @test + * @bug 8365575 * @summary Sanity test for AOTCache * @requires vm.cds.supports.aot.class.linking * @library /test/lib @@ -33,24 +34,72 @@ * @run driver VerifierFailOver */ +import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; public class VerifierFailOver { + + static final String mainClass = VerifierFailOverApp.class.getName(); + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + public static void main(String... args) throws Exception { SimpleCDSAppTester.of("VerifierFailOver") .addVmArgs("-Xlog:aot,aot+class=debug") .classpath("app.jar") .appCommandLine("VerifierFailOverApp") .setTrainingChecker((OutputAnalyzer out) -> { - out.shouldContain("Skipping VerifierFailOver_Helper: Verified with old verifier"); + out.shouldMatch("class.* klasses.* VerifierFailOver_Helper"); }) .setAssemblyChecker((OutputAnalyzer out) -> { - // classes verified with fail-over mode should not be cached. - out.shouldMatch("class.* klasses.* VerifierFailOverApp"); - out.shouldNotMatch("class.* klasses.* VerifierFailOver_Helper"); + // Classes verified with fail-over can be cached if AOTClassLinking is on + out.shouldMatch("class.* klasses.* VerifierFailOverApp aot-linked"); + out.shouldMatch("class.* klasses.* VerifierFailOver_Helper aot-linked"); }) .runAOTWorkflow(); + + + // When running an assembly run without AOTClassLinking, any classes verified with + // fail-over need to be excluded. + Tester t = new Tester(); + t.runAOTWorkflow(); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + if (runMode == RunMode.ASSEMBLY) { + return new String[] {"-XX:-AOTClassLinking", "-Xlog:aot,aot+class=debug"}; + } else { + return new String[] { "-Xlog:aot,aot+class=debug" }; + } + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { mainClass }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode == RunMode.TRAINING) { + out.shouldMatch("class.* klasses.* VerifierFailOver_Helper"); + } else if (runMode == RunMode.ASSEMBLY) { + out.shouldContain("Skipping VerifierFailOver_Helper: Old class has been linked"); + out.shouldMatch("class.* klasses.* VerifierFailOverApp"); + out.shouldNotMatch("class.* klasses.* VerifierFailOver_Helper"); + } + } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java index f7e15f8fae0..694e653676e 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java @@ -67,7 +67,7 @@ public class AddOpens { {{"-Xlog:cds", "-Xlog:cds"}, {"--add-opens", addOpensArg}}; private static String expectedOutput[] = - { "[class,load] com.simple.Main source: shared objects file", + { "\\[class,load *\\] com\\.simple\\.Main source: shared objects file", "method.setAccessible succeeded!"}; public static void buildTestModule() throws Exception { @@ -103,7 +103,7 @@ public class AddOpens { out.shouldContain("Full module graph = enabled"); }) .setProductionChecker((OutputAnalyzer out) -> { - out.shouldContain(expectedOutput[0]); + out.shouldMatch(expectedOutput[0]); out.shouldContain(expectedOutput[1]); }) .runStaticWorkflow() @@ -154,7 +154,7 @@ public class AddOpens { @Override public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { if (runMode == RunMode.PRODUCTION) { - out.shouldContain(expectedOutput[0]); + out.shouldMatch(expectedOutput[0]); out.shouldContain(expectedOutput[1]); } else if (runMode == RunMode.ASSEMBLY) { out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)"); @@ -197,7 +197,7 @@ public class AddOpens { @Override public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { if (runMode == RunMode.PRODUCTION) { - out.shouldContain(expectedOutput[0]); + out.shouldMatch(expectedOutput[0]); out.shouldContain(expectedOutput[1]); } else if (runMode == RunMode.ASSEMBLY) { out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/customLoader/OldClassAndInf.java b/test/hotspot/jtreg/runtime/cds/appcds/customLoader/OldClassAndInf.java index ea5acef8551..eb7f1dbf11f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/customLoader/OldClassAndInf.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/customLoader/OldClassAndInf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -92,7 +92,7 @@ public class OldClassAndInf { TestCommon.checkExec(output); for (String loadee : loadees) { - output.shouldContain("[class,load] " + loadee + " source: shared objects file"); + output.shouldMatch("\\[class,load *\\] " + loadee + " source: shared objects file"); } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchivedSuperIf.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchivedSuperIf.java index b46b48efb62..28d8043cde0 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchivedSuperIf.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchivedSuperIf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -74,7 +74,7 @@ public class ArchivedSuperIf extends DynamicArchiveTestBase { .assertNormalExit(output -> { // The interface Bar will be loaded from the archive. // The class (Baz) which implements Bar will be loaded from jar. - output.shouldContain("[class,load] pkg.Bar source: shared objects file (top)") + output.shouldMatch("\\[class,load *\\] pkg\\.Bar source: shared objects file \\(top\\)") .shouldMatch(".class.load. pkg.Baz source:.*archived_super_if.jar") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/JFRDynamicCDS.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/JFRDynamicCDS.java index 7abfd11b914..c6c30c164a1 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/JFRDynamicCDS.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/JFRDynamicCDS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, 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 @@ -62,7 +62,7 @@ public class JFRDynamicCDS extends DynamicArchiveTestBase { .assertNormalExit(output -> { output.shouldHaveExitValue(0) .shouldMatch(".class.load. jdk.jfr.events.*source:.*jrt:/jdk.jfr") - .shouldContain("[class,load] JFRDynamicCDSApp source: shared objects file (top)"); + .shouldMatch("\\[class,load *\\] JFRDynamicCDSApp source: shared objects file \\(top\\)"); }); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java index 9845fd9549b..5ff65f85493 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, 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 @@ -76,7 +76,7 @@ public class LambdaContainsOldInf extends DynamicArchiveTestBase { "-Xlog:class+load=debug", "-cp", appJar, mainClass, mainArg) .assertNormalExit(output -> { - output.shouldContain("[class,load] LambdaContainsOldInfApp source: shared objects file (top)") + output.shouldMatch("\\[class,load *\\] LambdaContainsOldInfApp source: shared objects file \\(top\\)") .shouldMatch(".class.load. OldProvider.source:.*lambda_contains_old_inf.jar") .shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda.*/0x.*source:.*LambdaContainsOldInf") .shouldHaveExitValue(0); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java index a43888c81cd..8f001209892 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java @@ -117,7 +117,7 @@ public class MainModuleOnly extends DynamicArchiveTestBase { "--module-path", moduleDir.toString(), "-m", TEST_MODULE1) .assertNormalExit(output -> { - output.shouldContain("[class,load] com.simple.Main source: shared objects file") + output.shouldMatch("\\[class,load *\\] com\\.simple\\.Main source: shared objects file") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/classpathtests/BootAppendTests.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/classpathtests/BootAppendTests.java index a77c38c21b9..8bb84424676 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/classpathtests/BootAppendTests.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/classpathtests/BootAppendTests.java @@ -205,7 +205,7 @@ public class BootAppendTests { "Test #6", BOOT_APPEND_CLASS, "true", "BOOT"); TestCommon.checkExec(output); if (!TestCommon.isUnableToMap(output)) - output.shouldContain("[class,load] sun.nio.cs.ext1.MyClass source: shared objects file"); + output.shouldMatch("\\[class,load *\\] sun\\.nio\\.cs\\.ext1\\.MyClass source: shared objects file"); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/AddModules.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/AddModules.java index bd569732ed5..9c2039e538b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/AddModules.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/AddModules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -118,8 +118,8 @@ public class AddModules { moduleDir.toString(), // --module-path MAIN_MODULE1) // -m .assertNormalExit(out -> { - out.shouldContain("[class,load] com.greetings.Main source: shared objects file") - .shouldContain("[class,load] org.astro.World source: shared objects file"); + out.shouldMatch("\\[class,load *\\] com\\.greetings\\.Main source: shared objects file") + .shouldMatch("\\[class,load *\\] org\\.astro\\.World source: shared objects file"); }); // run the com.hello module with the archive with the --module-path @@ -130,8 +130,8 @@ public class AddModules { moduleDir.toString(), // --module-path MAIN_MODULE2) // -m .assertNormalExit(out -> { - out.shouldContain("[class,load] com.hello.Main source: shared objects file") - .shouldContain("[class,load] org.astro.World source: shared objects file"); + out.shouldMatch("\\[class,load *\\] com\\.hello\\.Main source: shared objects file") + .shouldMatch("\\[class,load *\\] org\\.astro\\.World source: shared objects file"); }); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java index 6c44ad587e3..9985215f773 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -115,8 +115,8 @@ public class ModulePathAndCP { moduleDir.toString(), // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldContain("[class,load] com.greetings.Main source: shared objects file") - .shouldContain("[class,load] org.astro.World source: shared objects file"); + out.shouldMatch("\\[class,load *\\] com\\.greetings\\.Main source: shared objects file") + .shouldMatch("\\[class,load *\\] org\\.astro\\.World source: shared objects file"); }); // run with the archive with the --module-path different from the one during @@ -181,7 +181,7 @@ public class ModulePathAndCP { jars, // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldContain("[class,load] com.greetings.Main source: shared objects file") + out.shouldMatch("\\[class,load *\\] com\\.greetings\\.Main source: shared objects file") .shouldMatch(".class.load. org.astro.World source:.*org.astro.jar"); }); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java index 26bccdf764e..b55e6cfbd8b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.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 @@ -71,8 +71,8 @@ public class OldClassAndRedefineClass { "-Xlog:cds,class+load", agentCmdArg, "OldClassAndRedefineClassApp"); - out.shouldContain("[class,load] OldSuper source: shared objects file") - .shouldContain("[class,load] ChildOldSuper source: shared objects file") - .shouldContain("[class,load] Hello source: __VM_RedefineClasses__"); + out.shouldMatch("\\[class,load *\\] OldSuper source: shared objects file") + .shouldMatch("\\[class,load *\\] ChildOldSuper source: shared objects file") + .shouldMatch("\\[class,load *\\] Hello source: __VM_RedefineClasses__"); } } diff --git a/test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java b/test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java new file mode 100644 index 00000000000..130b7f539ab --- /dev/null +++ b/test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java @@ -0,0 +1,88 @@ +/* + * 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 Very large StackMapTable should cause OutOfMemoryError and not VM crash. + * @bug 8386562 + * @library /test/lib /test/hotspot/jtreg/testlibrary/asm + * @run main StackMapTooLong + */ + +import java.lang.invoke.MethodHandles; +import static org.objectweb.asm.Opcodes.*; +import org.objectweb.asm.*; + +public class StackMapTooLong { + private static String BadClass = "BadClass"; + + public static void main(String[] args) throws Exception { + byte[] classFile = dumpBadClass(); + + try { + MethodHandles.lookup().defineClass(classFile); + throw new RuntimeException("OutOfMemoryError expected but not thrown!"); + } catch (OutOfMemoryError expected) {} + } + + static class LargeStackMapTable extends Attribute { + LargeStackMapTable() { + super("StackMapTable"); + } + + @Override + public boolean isCodeAttribute() { + return true; + } + + @Override + protected ByteVector write(ClassWriter cw, byte[] code, + int codeLength, int maxStack, int maxLocals) { + int len = 16 * 1024 * 1024 + 1; // Too large to be allocated by Metaspace::allocate() + ByteVector bv = new ByteVector(); + bv.putByteArray(new byte[len], 0, len); + return bv; + } + } + + private static byte[] dumpBadClass() throws Exception { + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit(51, ACC_SUPER, BadClass, null, "java/lang/Object", + null); + + { + methodVisitor = + classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", + "([Ljava/lang/String;)V", null, null); + methodVisitor.visitCode(); + methodVisitor.visitInsn(RETURN); + methodVisitor.visitAttribute(new LargeStackMapTable()); + methodVisitor.visitMaxs(0, 1); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } +} diff --git a/test/hotspot/jtreg/runtime/jni/mutateFinals/MutateFinalsTest.java b/test/hotspot/jtreg/runtime/jni/mutateFinals/MutateFinalsTest.java index 0ae4ae231b9..69786e3b368 100644 --- a/test/hotspot/jtreg/runtime/jni/mutateFinals/MutateFinalsTest.java +++ b/test/hotspot/jtreg/runtime/jni/mutateFinals/MutateFinalsTest.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 @@ -134,7 +134,7 @@ class MutateFinalsTest { void testMutateInstanceFinalWithLogging(String methodName) throws Exception { String type = methodName.contains("Object") ? "Object" : ""; test(methodName, "-Xlog:jni=debug") - .shouldContain("[debug][jni] Set" + type + "Field mutated final instance field") + .shouldMatch("\\[debug *\\]\\[jni *\\] Set" + type + "Field mutated final instance field") .shouldHaveExitValue(0); } @@ -147,7 +147,7 @@ class MutateFinalsTest { void testMutateStaticFinalWithLogging(String methodName) throws Exception { String type = methodName.contains("Object") ? "Object" : ""; test(methodName, "-Xlog:jni=debug") - .shouldContain("[debug][jni] SetStatic" + type + "Field mutated final static field") + .shouldMatch("\\[debug *\\]\\[jni *\\] SetStatic" + type + "Field mutated final static field") .shouldHaveExitValue(0); } diff --git a/test/hotspot/jtreg/runtime/logging/ClassInitializationTest.java b/test/hotspot/jtreg/runtime/logging/ClassInitializationTest.java index 34249e5fec8..ab076826723 100644 --- a/test/hotspot/jtreg/runtime/logging/ClassInitializationTest.java +++ b/test/hotspot/jtreg/runtime/logging/ClassInitializationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, 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 @@ -60,7 +60,7 @@ public class ClassInitializationTest { "BadMap50"); out = new OutputAnalyzer(pb.start()); out.shouldNotHaveExitValue(0); - out.shouldNotContain("[class,init]"); + out.shouldNotMatch("\\[class,init *\\]"); out.shouldNotContain("Fail over class verification to old verifier for: BadMap50"); } diff --git a/test/hotspot/jtreg/runtime/logging/CondyIndyTest.java b/test/hotspot/jtreg/runtime/logging/CondyIndyTest.java index c62402c918b..9e75d52496f 100644 --- a/test/hotspot/jtreg/runtime/logging/CondyIndyTest.java +++ b/test/hotspot/jtreg/runtime/logging/CondyIndyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -44,27 +44,27 @@ public class CondyIndyTest { "CondyIndy"); OutputAnalyzer o = new OutputAnalyzer(pb.start()); o.shouldHaveExitValue(0); - o.shouldContain("[info][methodhandles"); - o.shouldNotContain("[debug][methodhandles,indy"); - o.shouldNotContain("[debug][methodhandles,condy"); + o.shouldMatch("\\[info *\\]\\[methodhandles"); + o.shouldNotMatch("\\[debug *\\]\\[methodhandles,indy"); + o.shouldNotMatch("\\[debug *\\]\\[methodhandles,condy"); // (2) methodhandles+condy=debug only pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:methodhandles+condy=debug", "CondyIndy"); o = new OutputAnalyzer(pb.start()); o.shouldHaveExitValue(0); - o.shouldNotContain("[info ][methodhandles"); - o.shouldNotContain("[debug][methodhandles,indy"); - o.shouldContain("[debug][methodhandles,condy"); + o.shouldNotMatch("\\[info *\\]\\[methodhandles"); + o.shouldNotMatch("\\[debug *\\]\\[methodhandles,indy"); + o.shouldMatch("\\[debug *\\]\\[methodhandles,condy"); // (3) methodhandles+indy=debug only pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:methodhandles+indy=debug", "CondyIndy"); o = new OutputAnalyzer(pb.start()); o.shouldHaveExitValue(0); - o.shouldNotContain("[info ][methodhandles"); - o.shouldContain("[debug][methodhandles,indy"); - o.shouldNotContain("[debug][methodhandles,condy"); + o.shouldNotMatch("\\[info *\\]\\[methodhandles"); + o.shouldMatch("\\[debug *\\]\\[methodhandles,indy"); + o.shouldNotMatch("\\[debug *\\]\\[methodhandles,condy"); // (4) methodhandles, condy, indy all on pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:methodhandles=info", @@ -73,8 +73,8 @@ public class CondyIndyTest { "CondyIndy"); o = new OutputAnalyzer(pb.start()); o.shouldHaveExitValue(0); - o.shouldContain("[info ][methodhandles"); - o.shouldContain("[debug][methodhandles,indy"); - o.shouldContain("[debug][methodhandles,condy"); + o.shouldMatch("\\[info *\\]\\[methodhandles"); + o.shouldMatch("\\[debug *\\]\\[methodhandles,indy"); + o.shouldMatch("\\[debug *\\]\\[methodhandles,condy"); }; } diff --git a/test/hotspot/jtreg/runtime/logging/ExceptionsTest.java b/test/hotspot/jtreg/runtime/logging/ExceptionsTest.java index c2c966cb047..3bbec57b158 100644 --- a/test/hotspot/jtreg/runtime/logging/ExceptionsTest.java +++ b/test/hotspot/jtreg/runtime/logging/ExceptionsTest.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 @@ -68,7 +68,7 @@ public class ExceptionsTest { static void analyzeOutputOff(ProcessBuilder pb) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("[exceptions]"); + output.shouldNotMatch("\\[exceptions *\\]"); output.shouldHaveExitValue(0); } diff --git a/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java index 6e114ff6b1d..c16102ff448 100644 --- a/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java +++ b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java @@ -43,7 +43,7 @@ public class GenerateOopMapTest { static String infoPattern = "[generateoopmap]"; static String debugPattern = "[generateoopmap] Basicblock#0 begins at:"; - static String tracePattern = "[trace][generateoopmap] 5 vars = 'r' stack = 'v' monitors = '' \tifne"; + static String tracePattern = "\\[trace *\\]\\[generateoopmap\\] 5 vars = 'r' stack = 'v' monitors = '' \tifne"; static String traceDetailPattern = "[generateoopmap] 0 vars = ( r |slot0) invokestatic()V"; static void test() throws Exception { @@ -63,7 +63,7 @@ public class GenerateOopMapTest { pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", "GenerateOopMapTest", "test"); o = new OutputAnalyzer(pb.start()); - o.shouldContain(tracePattern).shouldHaveExitValue(0); + o.shouldMatch(tracePattern).shouldHaveExitValue(0); // Prints extra stuff with detailed. Not sure how useful this is but keep it for now. if (Platform.isDebugBuild()) { diff --git a/test/hotspot/jtreg/runtime/logging/RelocatorTest.java b/test/hotspot/jtreg/runtime/logging/RelocatorTest.java new file mode 100644 index 00000000000..edbe71f2638 --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/RelocatorTest.java @@ -0,0 +1,84 @@ +/* + * 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 8197901 8209758 + * @summary Log relocation in class redefinition. + * @library /test/lib + * @modules java.compiler + * java.instrument + * @requires vm.jvmti + * @run main RedefineClassHelper + * @run driver RelocatorTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +// package access top-level class to avoid problem with RedefineClassHelper +// and nested types. +class RelocatorTest_B { + public static void test() { + System.out.println("Old class"); + } +} + +public class RelocatorTest { + public static class InternalClass { + public static String newB = + "class RelocatorTest_B {" + + " public static void test() { " + + " System.out.println(\"New class\");" + + " System.out.println(\"Need more ldc's in this class\");" + + " System.out.println(\"Another ldc\");" + + " }" + + "}"; + + public static void main(String[] args) throws Exception { + RelocatorTest_B.test(); + RedefineClassHelper.redefineClass(RelocatorTest_B.class, newB); + RelocatorTest_B.test(); + } + } + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-javaagent:redefineagent.jar", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+StressLdcRewrite", + "-Xlog:relocator=trace,redefine+class+constantpool=trace", + InternalClass.class.getName()); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldContain("Old class"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] Space at: 3 Size: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] \\{method\\} .* 'test' '\\(\\)V' in 'RelocatorTest_B'"); + output.shouldMatch("\\[trace\\]\\[relocator *\\] ChangeWiden. bci: 3 New_ilen: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] Space at: 12 Size: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] \\{method\\} .* 'test' '\\(\\)V' in 'RelocatorTest_B'"); + output.shouldMatch("\\[trace\\]\\[relocator *\\] ChangeWiden. bci: 12 New_ilen: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] Space at: 21 Size: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] \\{method\\} .* 'test' '\\(\\)V' in 'RelocatorTest_B'"); + output.shouldContain("New class"); + output.shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/runtime/logging/StackWalkTest.java b/test/hotspot/jtreg/runtime/logging/StackWalkTest.java index 226dac6f466..1cf90c1088d 100644 --- a/test/hotspot/jtreg/runtime/logging/StackWalkTest.java +++ b/test/hotspot/jtreg/runtime/logging/StackWalkTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, 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 @@ -44,7 +44,7 @@ public class StackWalkTest { static void analyzeOutputOff(ProcessBuilder pb) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("[stackwalk]"); + output.shouldNotMatch("\\[stackwalk *\\]"); output.shouldHaveExitValue(0); } diff --git a/test/hotspot/jtreg/runtime/logging/StartupTimeTest.java b/test/hotspot/jtreg/runtime/logging/StartupTimeTest.java index 88ec76efd12..a1715509243 100644 --- a/test/hotspot/jtreg/runtime/logging/StartupTimeTest.java +++ b/test/hotspot/jtreg/runtime/logging/StartupTimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, 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 @@ -47,7 +47,7 @@ public class StartupTimeTest { static void analyzeOutputOff(ProcessBuilder pb) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("[startuptime]"); + output.shouldNotMatch("\\[startuptime *\\]"); output.shouldHaveExitValue(0); } diff --git a/test/hotspot/jtreg/runtime/logging/VerificationTest.java b/test/hotspot/jtreg/runtime/logging/VerificationTest.java index a6a0ee66acc..b2315f585c8 100644 --- a/test/hotspot/jtreg/runtime/logging/VerificationTest.java +++ b/test/hotspot/jtreg/runtime/logging/VerificationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, 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 @@ -39,26 +39,26 @@ public class VerificationTest { static void analyzeOutputOn(ProcessBuilder pb, boolean isLogLevelInfo) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldContain("[verification]"); + output.shouldMatch("\\[verification *\\]"); output.shouldContain("Verifying class VerificationTest$InternalClass with new format"); output.shouldContain("Verifying method VerificationTest$InternalClass.()V"); output.shouldContain("End class verification for: VerificationTest$InternalClass"); if (isLogLevelInfo) { // logging level 'info' should not output stack map and opcode data. - output.shouldNotContain("[verification] StackMapTable: frame_count"); - output.shouldNotContain("[verification] offset = 0, opcode ="); + output.shouldNotMatch("\\[verification *\\] StackMapTable: frame_count"); + output.shouldNotMatch("\\[verification *\\] offset = 0, opcode ="); } else { // log level debug - output.shouldContain("[debug][verification] StackMapTable: frame_count"); - output.shouldContain("[debug][verification] offset = 0, opcode ="); + output.shouldMatch("\\[debug *\\]\\[verification *\\] StackMapTable: frame_count"); + output.shouldMatch("\\[debug *\\]\\[verification *\\] offset = 0, opcode ="); } output.shouldHaveExitValue(0); } static void analyzeOutputOff(ProcessBuilder pb) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("[verification]"); + output.shouldNotMatch("\\[verification *\\]"); output.shouldHaveExitValue(0); } diff --git a/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleTraceCL.java b/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleTraceCL.java index 4750e95c828..0c57b4f2076 100644 --- a/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleTraceCL.java +++ b/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleTraceCL.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 @@ -60,11 +60,11 @@ public class PatchModuleTraceCL { OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldHaveExitValue(0); // "modules" jimage case. - output.shouldContain("[class,load] java.lang.Thread source: jrt:/java.base"); + output.shouldMatch("\\[class,load *\\] java\\.lang\\.Thread source: jrt:/java.base"); // --patch-module case. - output.shouldContain("[class,load] javax.naming.spi.NamingManager source: mods/java.naming"); + output.shouldMatch("\\[class,load *\\] javax\\.naming\\.spi\\.NamingManager source: mods/java.naming"); // -cp case. - output.shouldContain("[class,load] PatchModuleMain source: file"); + output.shouldMatch("\\[class,load *\\] PatchModuleMain source: file"); // Test -Xlog:class+load=info output for -Xbootclasspath/a source = "package PatchModuleTraceCL_pkg; " + @@ -82,7 +82,7 @@ public class PatchModuleTraceCL { "-Xlog:class+load=info", "PatchModuleMain", "PatchModuleTraceCL_pkg.ItIsI"); output = new OutputAnalyzer(pb.start()); // -Xbootclasspath/a case. - output.shouldContain("[class,load] PatchModuleTraceCL_pkg.ItIsI source: xbcp"); + output.shouldMatch("\\[class,load *\\] PatchModuleTraceCL_pkg\\.ItIsI source: xbcp"); output.shouldHaveExitValue(0); } } diff --git a/test/hotspot/jtreg/runtime/os/THPsInThreadStackPreventionTest.java b/test/hotspot/jtreg/runtime/os/THPsInThreadStackPreventionTest.java index 163a665b687..7fa80a5b40b 100644 --- a/test/hotspot/jtreg/runtime/os/THPsInThreadStackPreventionTest.java +++ b/test/hotspot/jtreg/runtime/os/THPsInThreadStackPreventionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -188,7 +188,7 @@ public class THPsInThreadStackPreventionTest { output.shouldHaveExitValue(0); // this line indicates the mitigation is active: - output.shouldContain("[pagesize] JVM will attempt to prevent THPs in thread stacks."); + output.shouldMatch("\\[pagesize *\\] JVM will attempt to prevent THPs in thread stacks\\."); ProcSelfStatus status = ProcSelfStatus.parse(output); if (status.numLifeThreads < numThreads) { @@ -225,7 +225,7 @@ public class THPsInThreadStackPreventionTest { output.shouldHaveExitValue(0); // We deliberately switched off mitigation, VM should tell us: - output.shouldContain("[pagesize] JVM will *not* prevent THPs in thread stacks. This may cause high RSS."); + output.shouldMatch("\\[pagesize *\\] JVM will \\*not\\* prevent THPs in thread stacks\\. This may cause high RSS\\."); // Parse output from self/status ProcSelfStatus status = ProcSelfStatus.parse(output); diff --git a/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java b/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java index 6c5896df058..52e7560a460 100644 --- a/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java +++ b/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java @@ -68,8 +68,8 @@ import java.util.Set; public class TestHugePageDecisionsAtVMStartup { // End user warnings, printing with Xlog:pagesize at warning level, should be unconditional - static final String warningNoTHP = "[warning][pagesize] UseTransparentHugePages disabled, transparent huge pages are not supported by the operating system."; - static final String warningNoLP = "[warning][pagesize] UseLargePages disabled, no large pages configured and available on the system."; + static final String warningNoTHP = "\\[warning\\]\\[pagesize *\\] UseTransparentHugePages disabled, transparent huge pages are not supported by the operating system\\."; + static final String warningNoLP = "\\[warning\\]\\[pagesize *\\] UseLargePages disabled, no large pages configured and available on the system\\."; static final String buildSizeString(long l) { String units[] = { "K", "M", "G" }; @@ -119,27 +119,27 @@ public class TestHugePageDecisionsAtVMStartup { } if (!useLP) { - out.shouldContain("[info][pagesize] Large page support disabled"); + out.shouldMatch("\\[info *\\]\\[pagesize *\\] Large page support disabled"); } else if (useLP && !useTHP && (!configuration.supportsExplicitHugePages() || !haveUsableExplicitHugePages)) { - out.shouldContain(warningNoLP); + out.shouldMatch(warningNoLP); } else if (useLP && useTHP && !configuration.supportsTHP()) { - out.shouldContain(warningNoTHP); + out.shouldMatch(warningNoTHP); } else if (useLP && !useTHP && configuration.supportsExplicitHugePages() && haveUsableExplicitHugePages) { if (configuration.getExplicitAvailableHugePageNumber() == 0) { throw new SkippedException("No usable explicit hugepages configured on the system, skipping test"); } - out.shouldContain("[info][pagesize] Using the default large page size: " + buildSizeString(configuration.getExplicitDefaultHugePageSize())); - out.shouldContain("[info][pagesize] UseLargePages=1, UseTransparentHugePages=0"); - out.shouldContain("[info][pagesize] Large page support enabled"); + out.shouldMatch("\\[info *\\]\\[pagesize *\\] Using the default large page size: " + buildSizeString(configuration.getExplicitDefaultHugePageSize())); + out.shouldMatch("\\[info *\\]\\[pagesize *\\] UseLargePages=1, UseTransparentHugePages=0"); + out.shouldMatch("\\[info *\\]\\[pagesize *\\] Large page support enabled"); } else if (useLP && useTHP && configuration.supportsTHP()) { long thpPageSize = configuration.getThpPageSizeOrFallback(); String thpPageSizeString = buildSizeString(thpPageSize); // We expect to see exactly two "Usable page sizes" : the system page size and the THP page size. The system // page size differs, but its always in KB). - out.shouldContain("[info][pagesize] UseLargePages=1, UseTransparentHugePages=1"); - out.shouldMatch(".*\\[info]\\[pagesize] Large page support enabled. Usable page sizes: \\d+[kK], " + thpPageSizeString + ". Default large page size: " + thpPageSizeString + ".*"); + out.shouldMatch("\\[info *\\]\\[pagesize *\\] UseLargePages=1, UseTransparentHugePages=1"); + out.shouldMatch("\\[info *\\]\\[pagesize *\\] Large page support enabled\\. Usable page sizes: \\d+[kK], " + thpPageSizeString + "\\. Default large page size: " + thpPageSizeString); } } diff --git a/test/hotspot/jtreg/runtime/os/TestTrimNative.java b/test/hotspot/jtreg/runtime/os/TestTrimNative.java index e8645a91479..467891bfe1e 100644 --- a/test/hotspot/jtreg/runtime/os/TestTrimNative.java +++ b/test/hotspot/jtreg/runtime/os/TestTrimNative.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 SAP SE. All rights reserved. * Copyright (c) 2023, 2024, Red Hat, Inc. All rights reserved. - * Copyright (c) 2023, 2024, 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 @@ -322,7 +322,7 @@ public class TestTrimNative { checkExpectedLogMessages(output, false, 0); parseOutputAndLookForNegativeTrim(output, 0, 0, strictTesting); // The following output is expected to be printed with warning level, so it should not need -Xlog - output.shouldContain("[warning][trimnative] Native heap trim is not supported on this platform"); + output.shouldMatch("\\[warning\\]\\[trimnative *\\] Native heap trim is not supported on this platform"); } break; case "testOffExplicit": { diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java index 11e43b8d630..f93bfb8b3a3 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java @@ -75,6 +75,7 @@ public class PerfMapTest { // Sanity check the file contents boolean sawCallStub = false; + boolean sawVtableStub = false; try { for (String entry : Files.readAllLines(path)) { Matcher m = LINE_PATTERN.matcher(entry); @@ -82,12 +83,17 @@ public class PerfMapTest { if (m.group(3).contains("StubRoutines call_stub")) { sawCallStub = true; } + if (m.group(3).contains("vtable stub [")) { + sawVtableStub = true; + } } } catch (IOException e) { Assert.fail(e.toString()); } Assert.assertTrue(sawCallStub, "Expected StubRoutines call_stub entry in " + path); + Assert.assertTrue(sawVtableStub, + "Expected vtable stub entry in " + path); } @Test diff --git a/test/hotspot/jtreg/serviceability/dcmd/gc/HeapDumpParallelTest.java b/test/hotspot/jtreg/serviceability/dcmd/gc/HeapDumpParallelTest.java index 0010c5bdd78..f4dcb4dc1b0 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/gc/HeapDumpParallelTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/gc/HeapDumpParallelTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Alibaba Group Holding Limited. 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 @@ -55,7 +56,7 @@ public class HeapDumpParallelTest { dcmdOut.shouldHaveExitValue(0); dcmdOut.shouldContain("Heap dump file created"); OutputAnalyzer appOut = new OutputAnalyzer(app.getProcessStdout()); - appOut.shouldContain("[heapdump]"); + appOut.shouldMatch("\\[heapdump *\\]"); String opts = Arrays.asList(Utils.getTestJavaOpts()).toString(); if (opts.contains("-XX:+UseSerialGC") || opts.contains("-XX:+UseEpsilonGC")) { System.out.println("UseSerialGC detected."); @@ -136,4 +137,4 @@ public class HeapDumpParallelTest { PidJcmdExecutor executor = new PidJcmdExecutor("" + lingeredAppPid); return executor.execute("GC.heap_dump " + arg + " " + heapDumpFile.getAbsolutePath()); } -} \ No newline at end of file +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java new file mode 100644 index 00000000000..09073d99bfb --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java @@ -0,0 +1,77 @@ +/* + * 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 8387718 + * @summary VM_GetOrSetLocal slot bounds check overflows for long/double slots, + * allowing an out-of-bounds StackValueCollection access when slot == INT_MAX. + * @requires vm.jvmti + * @compile GetSetLocalSlotOverflow.java + * @run main/othervm/native -agentlib:GetSetLocalSlotOverflow GetSetLocalSlotOverflow + */ + +/* + * Regression test / reproducer for the signed-overflow in + * VM_BaseGetOrSetLocal::check_slot_type_no_lvt (jvmtiImpl.cpp). + * + * For a T_LONG/T_DOUBLE access, the bounds check is + * if (_index < 0 || _index + extra_slot >= method->max_locals()) + * with extra_slot == 1. When the agent passes slot == INT_MAX, the + * sub-expression _index + extra_slot overflows to INT_MIN, which is < max_locals(), + * so the guard passes and the code goes on to index locals->at(INT_MAX). + * + * Expected (fixed) behavior: GetLocalLong/Double and SetLocalLong/Double with + * slot == INT_MAX return JVMTI_ERROR_INVALID_SLOT. + * + * On an unfixed VM this test does not merely fail: the out-of-bounds access + * crashes the VM (assertion failure in fastdebug, SIGSEGV / silent corruption + * in product). A clean PASS is only possible once the bounds check is fixed. + */ + +public class GetSetLocalSlotOverflow { + + // Invoked from runner(); the agent inspects the runner() frame at depth 1. + // Returns false if any accessor did not return JVMTI_ERROR_INVALID_SLOT. + static native boolean testOverflow(Thread thread); + + public static void main(String[] args) throws Exception { + if (!runner()) { + throw new RuntimeException("Test GetSetLocalSlotOverflow failed"); + } + } + + // A Java frame holding a few locals. The agent targets this frame (depth 1) + // with slot == INT_MAX. The actual local contents are irrelevant: the + // overflow happens in the slot bounds check, before any local is read. + public static boolean runner() { + long l = 0xCAFEBABEL; + double d = 3.14d; + boolean ok = testOverflow(Thread.currentThread()); + // Keep locals live across the native call. + if (l == 0 && d == 0) { + throw new AssertionError("unreachable"); + } + return ok; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp new file mode 100644 index 00000000000..98872ddf0de --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp @@ -0,0 +1,113 @@ +/* + * 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 +#include +#include "jvmti.h" +#include "jvmti_common.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +// The runner() frame at depth 1; INT_MAX makes (slot + extra_slot) overflow +// for the long/double accessors. +static const jint Depth = 1; +static const jint OverflowSlot = INT_MAX; // 0x7fffffff + +static jvmtiEnv *jvmti = nullptr; + +// Each access below MUST come back as JVMTI_ERROR_INVALID_SLOT. On an unfixed +// VM the overflowing bounds check is bypassed and the subsequent +// locals->at(INT_MAX) access crashes the VM before we ever see a return code. +static bool expect_invalid_slot(const char* what, jvmtiError err) { + if (err == JVMTI_ERROR_INVALID_SLOT) { + LOG(" PASS: %s returned JVMTI_ERROR_INVALID_SLOT (%d) for slot=INT_MAX\n", what, err); + return true; + } + LOG(" FAIL: %s returned %d for slot=INT_MAX, expected JVMTI_ERROR_INVALID_SLOT (%d)\n", + what, err, JVMTI_ERROR_INVALID_SLOT); + return false; +} + +JNIEXPORT jboolean JNICALL +Java_GetSetLocalSlotOverflow_testOverflow(JNIEnv *env, jclass cls, jobject thread) { + if (jvmti == nullptr) { + LOG("JVMTI client was not properly loaded!\n"); + return JNI_FALSE; + } + + jlong lval = 0; + jdouble dval = 0; + + // T_LONG / T_DOUBLE => extra_slot == 1 => INT_MAX + 1 overflows to INT_MIN. + bool ok = true; + ok &= expect_invalid_slot("GetLocalLong", jvmti->GetLocalLong(thread, Depth, OverflowSlot, &lval)); + ok &= expect_invalid_slot("GetLocalDouble", jvmti->GetLocalDouble(thread, Depth, OverflowSlot, &dval)); + ok &= expect_invalid_slot("SetLocalLong", jvmti->SetLocalLong(thread, Depth, OverflowSlot, (jlong)0)); + ok &= expect_invalid_slot("SetLocalDouble", jvmti->SetLocalDouble(thread, Depth, OverflowSlot, (jdouble)0)); + return ok ? JNI_TRUE : JNI_FALSE; +} + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jint res; + jvmtiError err; + static jvmtiCapabilities caps; + + res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == nullptr) { + LOG("Wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + caps.can_access_local_variables = 1; + + err = jvmti->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + LOG("AddCapabilities: unexpected error: %d\n", err); + return JNI_ERR; + } + err = jvmti->GetCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + LOG("GetCapabilities: unexpected error: %d\n", err); + return JNI_ERR; + } + if (!caps.can_access_local_variables) { + LOG("Warning: Access to local variables is not implemented\n"); + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +#ifdef __cplusplus +} +#endif diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java index 7330f4c061d..115326567d3 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java @@ -33,13 +33,13 @@ import java.util.concurrent.*; public class VThreadTest { - private static final String agentLib = "VThreadTest"; - static final int MSG_COUNT = 10*1000; static final SynchronousQueue QUEUE = new SynchronousQueue<>(); static native boolean check(); + static void log(String msg) { System.out.println(msg); } + static void producer(String msg) throws InterruptedException { int ii = 1; long ll = 2*(long)ii; @@ -54,7 +54,11 @@ public class VThreadTest { for (int i = 0; i < MSG_COUNT; i++) { producer("msg: "); } - } catch (InterruptedException e) { } + } catch (Throwable t) { + t.printStackTrace(System.out); + log("VThreadTest failed: PRODUCER caught a throwable: " + t); + System.exit(1); + } }; static final Runnable CONSUMER = () -> { @@ -62,7 +66,11 @@ public class VThreadTest { for (int i = 0; i < MSG_COUNT; i++) { String s = QUEUE.take(); } - } catch (InterruptedException e) { } + } catch (Throwable t) { + t.printStackTrace(System.out); + log("VThreadTest failed: CONSUMER caught a throwable: " + t); + System.exit(1); + } }; public static void test1() throws Exception { @@ -80,14 +88,6 @@ public class VThreadTest { } public static void main(String[] args) throws Exception { - try { - System.loadLibrary(agentLib); - } catch (UnsatisfiedLinkError ex) { - System.err.println("Failed to load " + agentLib + " lib"); - System.err.println("java.library.path: " + System.getProperty("java.library.path")); - throw ex; - } - VThreadTest obj = new VThreadTest(); obj.runTest(); } diff --git a/test/hotspot/jtreg/serviceability/sa/TestG1HeapRegion.java b/test/hotspot/jtreg/serviceability/sa/TestG1HeapRegion.java index 32cc80aeb69..9fbe022022c 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestG1HeapRegion.java +++ b/test/hotspot/jtreg/serviceability/sa/TestG1HeapRegion.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 @@ -22,8 +22,10 @@ */ import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import sun.jvm.hotspot.debugger.Address; import sun.jvm.hotspot.gc.g1.G1CollectedHeap; import sun.jvm.hotspot.gc.g1.G1HeapRegion; import sun.jvm.hotspot.HotSpotAgent; @@ -39,11 +41,13 @@ import jdk.test.lib.Utils; /** * @test + * @bug 8194249 * @library /test/lib * @requires vm.hasSA * @requires (os.arch != "riscv64" | !(vm.cpu.features ~= ".*qemu.*")) * @requires vm.gc.G1 * @modules jdk.hotspot.agent/sun.jvm.hotspot + * jdk.hotspot.agent/sun.jvm.hotspot.debugger * jdk.hotspot.agent/sun.jvm.hotspot.gc.g1 * jdk.hotspot.agent/sun.jvm.hotspot.memory * jdk.hotspot.agent/sun.jvm.hotspot.runtime @@ -59,12 +63,40 @@ public class TestG1HeapRegion { try { agent.attach(Integer.parseInt(pid)); - G1CollectedHeap heap = (G1CollectedHeap)VM.getVM().getUniverse().heap(); - G1HeapRegion hr = heap.hrm().heapRegionIterator().next(); - G1HeapRegion hrTop = heap.hrm().getByAddress(hr.top()); - Asserts.assertEquals(hr.top(), hrTop.top(), - "Address of G1HeapRegion does not match."); + G1CollectedHeap heap = (G1CollectedHeap)VM.getVM().getUniverse().heap(); + heap.printOn(System.out); + + // Print each region first. + System.out.println(); + Iterator hri = heap.hrm().heapRegionIterator(); + G1HeapRegion hr = hri.next(); + while (hr != null) { + hr.printOn(System.out); + hr = hri.next(); + } + System.out.println(); + + // Iterate over each region and confirm that getByAddress(top) returns + // the same address as the region being looked at. + hri = heap.hrm().heapRegionIterator(); + hr = hri.next(); + while (hr != null) { + hr.printOn(System.out); + Address top = hr.top(); + if (top.equals(hr.end())) { + // The end of the region is actually the first address after + // the end, so it points to the start of the next region. We need to + // subtract to avoid getByAddress(top) returning the next region. + top = top.addOffsetTo(-1); + } + G1HeapRegion hrTop = heap.hrm().getByAddress(top); + System.out.format("hr.top():0x%x <--> hrTop.top():0x%x\n", + hr.top().asLongValue(), hrTop.top().asLongValue()); + Asserts.assertEquals(hr.top(), hrTop.top(), + "Address of G1HeapRegion does not match."); + hr = hri.next(); + } } finally { agent.detach(); } @@ -76,6 +108,7 @@ public class TestG1HeapRegion { ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder( "--add-modules=jdk.hotspot.agent", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED", + "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.debugger=ALL-UNNAMED", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.gc.g1=ALL-UNNAMED", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.memory=ALL-UNNAMED", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED", diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java index 8d1d763a250..7fc3bc11dee 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.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 @@ -33,10 +33,12 @@ package verify.tests; import java.lang.foreign.*; +import java.util.Arrays; import java.util.Random; import jdk.test.lib.Utils; import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; import compiler.lib.verify.*; @@ -47,6 +49,8 @@ public class TestVerifyFloat16 { testArrayFloat16(); testRawFloat16(); testFloat16Random(); + testFloat16VectorCarrier(); + testFloat16VectorCarrierRandom(); } public static void testArrayFloat16() { @@ -113,6 +117,84 @@ public class TestVerifyFloat16 { } } + /** + * Exercises the {@code Float16Vector} short[]-carrier path in Verify + * (checkEQForFloat16Carrier). The short carrier bits encode Float16 values, so the + * non-raw mode must canonicalize NaN (distinct NaN encodings are equal) while the raw + * mode must compare the carrier bits directly (distinct NaN encodings are not equal). + */ + public static void testFloat16VectorCarrier() { + var species = Float16Vector.SPECIES_128; + int len = species.length(); + + // Two different NaN encodings of Float16. + short nan1 = (short)0xFFFF; + short nan2 = (short)0x7FFF; + + short[] aBits = new short[len]; + short[] bBits = new short[len]; + Arrays.fill(aBits, nan1); + Arrays.fill(bBits, nan2); + Float16Vector va = Float16Vector.fromArray(species, aBits, 0); + Float16Vector vb = Float16Vector.fromArray(species, bBits, 0); + + // Same vector: equal in both modes. + Verify.checkEQ(va, va); + Verify.checkEQWithRawBits(va, va); + + // Distinct NaN encodings: equal in non-raw mode (canonicalized) ... + Verify.checkEQ(va, vb); + // ... but not equal in raw mode. + checkNEWithRawBits(va, vb); + + // A real value mismatch must fail in both modes. + short[] oneBits = new short[len]; + short[] twoBits = new short[len]; + Arrays.fill(oneBits, Float16.float16ToShortBits(Float16.valueOf(1f))); + Arrays.fill(twoBits, Float16.float16ToShortBits(Float16.valueOf(2f))); + Float16Vector vOne = Float16Vector.fromArray(species, oneBits, 0); + Float16Vector vTwo = Float16Vector.fromArray(species, twoBits, 0); + Verify.checkEQ(vOne, vOne); + Verify.checkEQWithRawBits(vOne, vOne); + checkNE(vOne, vTwo); + checkNEWithRawBits(vOne, vTwo); + + // NaN vs a real number: not equal in either mode. + checkNE(va, vOne); + checkNEWithRawBits(va, vOne); + } + + public static void testFloat16VectorCarrierRandom() { + var species = Float16Vector.SPECIES_128; + int len = species.length(); + // Testing all 2^16 * 2^16 = 2^32 would take a bit long, so we randomly sample instead. + for (int i = 0; i < 10_000; i++) { + short bitsA = (short)RANDOM.nextInt(); + short bitsB = (short)RANDOM.nextInt(); + short[] aBits = new short[len]; + short[] bBits = new short[len]; + Arrays.fill(aBits, bitsA); + Arrays.fill(bBits, bitsB); + Float16Vector va = Float16Vector.fromArray(species, aBits, 0); + Float16Vector vb = Float16Vector.fromArray(species, bBits, 0); + + // Raw mode: equal iff identical carrier bits. + if (bitsA == bitsB) { + Verify.checkEQWithRawBits(va, vb); + } else { + checkNEWithRawBits(va, vb); + } + + // Non-raw mode: equal iff the canonicalized Float16 values match. + if (Float.floatToIntBits(Float.float16ToFloat(bitsA)) == + Float.floatToIntBits(Float.float16ToFloat(bitsB))) { + Verify.checkEQ(va, vb); + } else { + checkNE(va, vb); + } + } + } + public static void checkNE(Object a, Object b) { try { Verify.checkEQ(a, b); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java index 7866547177f..39dc949c233 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.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 @@ -201,7 +201,7 @@ public class thrstartreq001 { public void run() { try { - do { + while (true) { EventSet eventSet = vm.eventQueue().remove(1000); if (eventSet != null) { // there is not a timeout EventIterator it = eventSet.eventIterator(); @@ -219,11 +219,15 @@ public class thrstartreq001 { log.display("EventListener: following JDI event occured: " + event.toString()); } - if (isConnected) { - eventSet.resume(); - } + eventSet.resume(); + // Even if isConnected has been set false, we need to continue consuming + // events until there are no more. So do a continue here rather than + // allowing continuing to be conditional on isConnected below. + continue; } - } while (isConnected); + if (!isConnected) + break; + } } catch (InterruptedException e) { tot_res = FAILED; log.complain("FAILURE in EventListener: caught unexpected " diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java index 08bfad05a04..11ccfc9447c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.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 @@ -597,6 +597,9 @@ class CheckedFeatures { {"windows-x64", "com.sun.jdi.CommandLineLaunch", "dt_socket"}, {"windows-x64", "com.sun.jdi.RawCommandLineLaunch", "dt_socket"}, + {"windows-aarch64", "com.sun.jdi.CommandLineLaunch", "dt_socket"}, + {"windows-aarch64", "com.sun.jdi.RawCommandLineLaunch", "dt_socket"}, + {"macosx-amd64", "com.sun.jdi.CommandLineLaunch", "dt_shmem"}, {"macosx-amd64", "com.sun.jdi.RawCommandLineLaunch", "dt_shmem"}, diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java index 88b1de413dd..91a06a426c0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.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 @@ -242,6 +242,7 @@ public class JDIBase { ThreadStartEvent tse = (ThreadStartEvent) event; log2("ThreadStartEvent is received while waiting for a breakpoint" + " event, thread: : " + tse.thread().name()); + eventSet.resume(); continue; } diff --git a/test/jaxp/TEST.ROOT b/test/jaxp/TEST.ROOT index ddf29839e20..695645315d8 100644 --- a/test/jaxp/TEST.ROOT +++ b/test/jaxp/TEST.ROOT @@ -23,7 +23,7 @@ modules=java.xml groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index fcde1d9c01d..8879aa2e5b6 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -612,24 +612,6 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- # jdk_jfr -jdk/jfr/api/consumer/TestRecordingFileWrite.java 8286300 linux-s390x -jdk/jfr/api/consumer/streaming/TestCrossProcessStreaming.java 8286300 linux-s390x -jdk/jfr/api/consumer/streaming/TestFilledChunks.java 8286300 linux-s390x -jdk/jfr/api/consumer/streaming/TestRemovedChunks.java 8286300 linux-s390x -jdk/jfr/api/recording/misc/TestGetStreamWithFailure.java 8286300 linux-s390x -jdk/jfr/api/settings/TestSettingControl.java 8286300 linux-s390x -jdk/jfr/event/runtime/TestBackToBackSensitive.java 8286300 linux-s390x -jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java 8286300 linux-s390x -jdk/jfr/event/tracing/TestMultipleThreads.java 8286300 linux-s390x -jdk/jfr/event/tracing/TestTracedString.java 8286300 linux-s390x -jdk/jfr/javaagent/TestLoadedAgent.java 8286300 linux-s390x -jdk/jfr/javaagent/TestPremainAgent.java 8286300 linux-s390x -jdk/jfr/jmx/streaming/TestClose.java 8286300 linux-s390x -jdk/jfr/jmx/streaming/TestMaxSize.java 8286300 linux-s390x -jdk/jfr/jvm/TestChunkIntegrity.java 8286300 linux-s390x -jdk/jfr/jvm/TestJFRIntrinsic.java 8286300 linux-s390x -jdk/jfr/tool/TestDisassemble.java 8286300 linux-s390x -jdk/jfr/tool/TestScrub.java 8286300 linux-s390x jdk/jfr/event/compiler/TestCodeSweeper.java 8338127 generic-all jdk/jfr/event/oldobject/TestShenandoah.java 8342951 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 08a476427bb..08bc31ffdb8 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -59,7 +59,7 @@ exclusiveAccess.dirs=java/math/BigInteger/largeMemory \ java/rmi/Naming java/util/prefs sun/management/jmxremote \ sun/tools/jstatd sun/security/mscapi java/util/Arrays/largeMemory \ java/util/BitSet/stream javax/rmi java/net/httpclient/websocket \ -com/sun/net/httpserver/simpleserver sun/tools/jhsdb +com/sun/net/httpserver/simpleserver sun/tools/jhsdb javax/sound # Group definitions groups=TEST.groups @@ -120,7 +120,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/build/releaseFile/CheckReleaseFile.java b/test/jdk/build/releaseFile/CheckReleaseFile.java index 1160b616223..7a48ea530e3 100644 --- a/test/jdk/build/releaseFile/CheckReleaseFile.java +++ b/test/jdk/build/releaseFile/CheckReleaseFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, 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 @@ -112,6 +112,11 @@ public class CheckReleaseFile { } String valueString = valueMatcher.group(1); + if ("".equals(valueString)) { + throw new RuntimeException("The test failed, SOURCE value was empty." + + " The JDK workspace used for building might not have a .git directory or generated .src-rev file." + + " Verify that either .git was removed intentionally or generating .src-rev was intentionally omitted"); + } String[] values = valueString.split(" "); diff --git a/test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java b/test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java new file mode 100644 index 00000000000..386409a9f48 --- /dev/null +++ b/test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java @@ -0,0 +1,77 @@ +/* + * 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.util.List; +import java.util.ServiceLoader; +import java.util.ServiceLoader.Provider; + +import javax.naming.spi.InitialContextFactory; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/* + * @test + * @bug 8154193 + * @summary Verify that the com.sun.jndi.rmi.registry.RegistryContextFactory Service + * provided by the jdk.naming.rmi module isn't loaded by the boot loader + * @run junit ${test.main.class} + */ +class InitialContextFactoryClassLoaderTest { + + private static final String RMI_INITIAL_CTX_FACTORY_SERVICE = + "com.sun.jndi.rmi.registry.RegistryContextFactory"; + + /* + * Verifies that the javax.naming.spi.InitialContextFactory service provided by the + * jdk.naming.rmi module isn't loaded through the boot loader + */ + @Test + void testClassLoader() throws Exception { + final ServiceLoader serviceLoader = + ServiceLoader.load(InitialContextFactory.class, null); + final List> serviceTypes = serviceLoader + .stream() + .map(Provider::get) + .map(InitialContextFactory::getClass) + .toList(); + System.err.println("Found InitialContextFactory services: " + serviceTypes); + Class rmiInitialCtxService = null; + for (Class klass : serviceTypes) { + if (klass.getName().equals(RMI_INITIAL_CTX_FACTORY_SERVICE)) { + rmiInitialCtxService = klass; + break; // found the relevant service + } + } + // verify that the RMI InitialContextFactory service was found by the ServiceLoader + assertNotNull(rmiInitialCtxService, RMI_INITIAL_CTX_FACTORY_SERVICE + + " was not found by ServiceLoader"); + // now verify its module and the classloader + assertEquals("jdk.naming.rmi", rmiInitialCtxService.getModule().getName(), + "unexpected module for " + RMI_INITIAL_CTX_FACTORY_SERVICE + " class"); + // we don't expect the service to be loaded by boot loader + assertNotNull(rmiInitialCtxService.getClassLoader(), RMI_INITIAL_CTX_FACTORY_SERVICE + + " was unexpectedly loaded by boot loader"); + } +} diff --git a/test/jdk/com/sun/net/httpserver/Test9a.java b/test/jdk/com/sun/net/httpserver/Test9a.java deleted file mode 100644 index 56fbf9953a3..00000000000 --- a/test/jdk/com/sun/net/httpserver/Test9a.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 6270015 - * @library /test/lib - * @build jdk.test.lib.Utils - * jdk.test.lib.net.SimpleSSLContext - * jdk.test.lib.net.URIBuilder - * @run main/othervm Test9a - * @run main/othervm -Djava.net.preferIPv6Addresses=true Test9a - * @summary Light weight HTTP server - */ - -import com.sun.net.httpserver.*; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.*; -import java.io.*; -import java.net.*; -import javax.net.ssl.*; -import jdk.test.lib.net.SimpleSSLContext; -import jdk.test.lib.net.URIBuilder; - -import static jdk.test.lib.Asserts.assertEquals; -import static jdk.test.lib.Asserts.assertFileContentsEqual; -import static jdk.test.lib.Utils.createTempFileOfSize; - -/* Same as Test1 but requests run in parallel. - */ - -public class Test9a extends Test { - - private static final String TEMP_FILE_PREFIX = - HttpServer.class.getPackageName() + '-' + Test9a.class.getSimpleName() + '-'; - - private static final SSLContext serverCtx = SimpleSSLContext.findSSLContext(); - private static final SSLContext clientCtx = SimpleSSLContext.findSSLContext(); - static volatile boolean error = false; - - public static void main (String[] args) throws Exception { - HttpsServer server = null; - ExecutorService executor=null; - Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23); - Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088); - try { - System.out.print ("Test9a: "); - InetAddress loopback = InetAddress.getLoopbackAddress(); - InetSocketAddress addr = new InetSocketAddress(loopback, 0); - server = HttpsServer.create (addr, 0); - // Assert that both files share the same parent and can be served from the same `FileServerHandler` - assertEquals(smallFilePath.getParent(), largeFilePath.getParent()); - HttpHandler h = new FileServerHandler (smallFilePath.getParent().toString()); - HttpContext c1 = server.createContext ("/", h); - executor = Executors.newCachedThreadPool(); - server.setExecutor (executor); - server.setHttpsConfigurator(new HttpsConfigurator (serverCtx)); - server.start(); - - int port = server.getAddress().getPort(); - error = false; - Thread[] t = new Thread[100]; - - t[0] = test (true, "https", port, smallFilePath); - t[1] = test (true, "https", port, largeFilePath); - t[2] = test (true, "https", port, smallFilePath); - t[3] = test (true, "https", port, largeFilePath); - t[4] = test (true, "https", port, smallFilePath); - t[5] = test (true, "https", port, largeFilePath); - t[6] = test (true, "https", port, smallFilePath); - t[7] = test (true, "https", port, largeFilePath); - t[8] = test (true, "https", port, smallFilePath); - t[9] = test (true, "https", port, largeFilePath); - t[10] = test (true, "https", port, smallFilePath); - t[11] = test (true, "https", port, largeFilePath); - t[12] = test (true, "https", port, smallFilePath); - t[13] = test (true, "https", port, largeFilePath); - t[14] = test (true, "https", port, smallFilePath); - t[15] = test (true, "https", port, largeFilePath); - for (int i=0; i<16; i++) { - t[i].join(); - } - if (error) { - throw new RuntimeException ("error"); - } - - System.out.println ("OK"); - } finally { - if (server != null) - server.stop(0); - if (executor != null) - executor.shutdown(); - Files.delete(smallFilePath); - Files.delete(largeFilePath); - } - } - - static int foo = 1; - - static ClientThread test (boolean fixedLen, String protocol, int port, Path filePath) throws Exception { - ClientThread t = new ClientThread (fixedLen, protocol, port, filePath); - t.start(); - return t; - } - - static Object fileLock = new Object(); - - static class ClientThread extends Thread { - - boolean fixedLen; - String protocol; - int port; - private final Path filePath; - - ClientThread (boolean fixedLen, String protocol, int port, Path filePath) { - this.fixedLen = fixedLen; - this.protocol = protocol; - this.port = port; - this.filePath = filePath; - } - - public void run () { - try { - URL url = URIBuilder.newBuilder() - .scheme(protocol) - .loopback() - .port(port) - .path("/" + filePath.getFileName()) - .toURL(); - - HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); - if (urlc instanceof HttpsURLConnection) { - HttpsURLConnection urlcs = (HttpsURLConnection) urlc; - urlcs.setHostnameVerifier (new HostnameVerifier () { - public boolean verify (String s, SSLSession s1) { - return true; - } - }); - urlcs.setSSLSocketFactory (clientCtx.getSocketFactory()); - } - byte [] buf = new byte [4096]; - - String s = "chunk"; - if (fixedLen) { - urlc.setRequestProperty ("XFixed", "yes"); - s = "fixed"; - } - InputStream is = urlc.getInputStream(); - File temp; - synchronized (fileLock) { - temp = File.createTempFile (s, null); - temp.deleteOnExit(); - } - OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp)); - int c, count = 0; - while ((c=is.read(buf)) != -1) { - count += c; - fout.write (buf, 0, c); - } - is.close(); - fout.close(); - - if (count != filePath.toFile().length()) { - System.out.println ("wrong amount of data returned"); - System.out.println ("fixedLen = "+fixedLen); - System.out.println ("protocol = "+protocol); - System.out.println ("port = "+port); - System.out.println ("file = " + filePath); - System.out.println ("temp = "+temp); - System.out.println ("count = "+count); - error = true; - } - assertFileContentsEqual(filePath, temp.toPath()); - temp.delete(); - } catch (Exception e) { - e.printStackTrace(); - error = true; - } - } - } - -} diff --git a/test/jdk/com/sun/tools/attach/JvmTempDirTest.java b/test/jdk/com/sun/tools/attach/JvmTempDirTest.java new file mode 100644 index 00000000000..6729de149a6 --- /dev/null +++ b/test/jdk/com/sun/tools/attach/JvmTempDirTest.java @@ -0,0 +1,258 @@ +/* + * 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 com.sun.tools.attach.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; +import java.util.List; +import java.io.File; + +import jdk.test.lib.thread.ProcessThread; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8384557 + * @summary Test to make sure attach and jvmstat work correctly when -XX:AltTempDir is set. + * + * @requires os.family == "linux" + * @library /test/lib + * @modules jdk.attach + * jdk.jartool/sun.tools.jar + * + * @run build Application RunnerUtil + * @run main/timeout=200 JvmTempDirTest + */ + +/* + * This test is similar to TempDirTest.java. The property java.io.tmpdir does not affect how + * jdk.attach works, but -XX:AltTempDir does. + * + * This test runs with an extra long timeout since it takes a really long time with -Xcomp + * when starting many processes. + */ + +import jdk.test.lib.util.FileUtils; + +public class JvmTempDirTest { + + private static long startTime; + + public static void main(String args[]) throws Throwable { + + startTime = System.currentTimeMillis(); + + Path clientTmpDir = Files.createTempDirectory(Path.of("/tmp"), "c"); + Path targetTmpDir = Files.createTempDirectory(Path.of("/tmp"), "t"); + + try { + // Run the test with all possible combinations of setting AltTempDir. + // Different setting will cause the attach mechanism to fail. + String notFound = "not found in VM list"; + runExperiment(null, null, true, null); + runExperiment(targetTmpDir, targetTmpDir, true, null); + runExperiment(clientTmpDir, clientTmpDir, true, null); + + runExperiment(clientTmpDir, null, false, notFound); + runExperiment(clientTmpDir, targetTmpDir, false, notFound); + runExperiment(null, targetTmpDir, false, notFound); + } finally { + FileUtils.deleteFileTreeWithRetry(clientTmpDir); + FileUtils.deleteFileTreeWithRetry(targetTmpDir); + } + + String name = String.valueOf('a').repeat(200); + Path veryLongDir = Files.createTempDirectory(Path.of("/tmp"), name); + try { + runExperiment(veryLongDir, veryLongDir, false, "Socket file path too long"); + } finally { + FileUtils.deleteFileTreeWithRetry(veryLongDir); + } + + // Test a directory with only proc in one part of the name. + Path procTempDir = Files.createTempDirectory(Path.of("/tmp"), "proc"); + Path procDir = Files.createDirectory(procTempDir.resolve("proc")); + try { + runExperiment(procDir, procDir, true, null); + } finally { + FileUtils.deleteFileTreeWithRetry(procDir); + FileUtils.deleteFileTreeWithRetry(procTempDir); + } + + Path hsperfDir = Files.createTempDirectory(Path.of("/tmp"), "hsperfdata_"); + try { + runExperiment(hsperfDir, hsperfDir, true, null); + } finally { + FileUtils.deleteFileTreeWithRetry(hsperfDir); + } + + // Create /tmp/tmp, and try to use /tmp/tmp/noexist + Path tmpDir = Files.createTempDirectory(Path.of("/tmp"), "tmp"); + try { + Path noExist = tmpDir.resolve("noexist"); + runNoExistTest(noExist); + } finally { + FileUtils.deleteFileTreeWithRetry(tmpDir); + } + + Path relativeDir = Files.createTempDirectory(Path.of("."), "a"); + try { + runRelativeTest(relativeDir); + } finally { + FileUtils.deleteFileTreeWithRetry(relativeDir); + } + } + + /* + * The actual test is in the nested class TestMain. + * The responsibility of this class is to: + * 1. Start the Application class in a separate process. + * 2. Find the pid and shutdown port of the running Application. + * 3. Launch the tests in nested class TestMain that will attach to the Application. + * 4. Shut down the Application. + */ + public static void runExperiment(Path clientTmpDir, Path targetTmpDir, boolean shouldPass, String message) throws Throwable { + + System.out.print("### Running tests with overridden tmpdir for"); + System.out.print(" client: " + (clientTmpDir == null ? "no" : "yes")); + System.out.print(" target: " + (targetTmpDir == null ? "no" : "yes")); + System.out.println(" ###"); + + long elapsedTime = (System.currentTimeMillis() - startTime) / 1000; + System.out.println("Started after " + elapsedTime + "s"); + + ProcessThread processThread = null; + try { + String[] tmpDirArg = null; + if (targetTmpDir != null) { + tmpDirArg = new String[] {"-XX:AltTempDir=" + targetTmpDir}; + } + processThread = RunnerUtil.startApplication(tmpDirArg); + launchTests(processThread.getPid(), clientTmpDir, shouldPass, message); + } catch (Throwable t) { + System.out.println("JvmTempDirTest got unexpected exception: " + t); + t.printStackTrace(); + throw t; + } finally { + // Make sure the Application process is stopped. + RunnerUtil.stopApplication(processThread); + } + + elapsedTime = (System.currentTimeMillis() - startTime) / 1000; + System.out.println("Completed after " + elapsedTime + "s"); + + } + + /** + * Runs the actual tests in nested class TestMain. + * The reason for running the tests in a separate process + * is that we need to modify the class path and + * the -XX:AltTempDir argument. + */ + private static void launchTests(long pid, Path clientTmpDir, boolean shouldPass, String message) throws Throwable { + + String classpath = + System.getProperty("test.class.path", ""); + + String[] tmpDirArg = null; + if (clientTmpDir != null) { + tmpDirArg = new String [] {"-XX:AltTempDir=" + clientTmpDir}; + } + + // Arguments : [-XX:AltTempDir=] -classpath cp JvmTempDirTest$TestMain pid + String[] args = RunnerUtil.concat( + tmpDirArg, + new String[] { + "-classpath", + classpath, + "JvmTempDirTest$TestMain", + Long.toString(pid) }); + OutputAnalyzer output = ProcessTools.executeTestJava(args); + if (shouldPass) { + output.shouldHaveExitValue(0); + } else { + output.shouldContain(message); + output.shouldNotHaveExitValue(0); + } + } + + /** + * This is the actual test. It will attach to the running Application + * and perform a number of basic attach tests. + */ + public static class TestMain { + public static void main(String args[]) throws Exception { + String pid = args[0]; + + // Test 1 - list method should list the target VM + System.out.println(" - Test: VirtualMachine.list"); + List l = VirtualMachine.list(); + boolean found = false; + for (VirtualMachineDescriptor vmd: l) { + if (vmd.id().equals(pid)) { + found = true; + break; + } + } + if (found) { + System.out.println(" - " + pid + " found."); + } else { + throw new RuntimeException(pid + " not found in VM list"); + } + + // Test 2 - try to attach and verify connection + + System.out.println(" - Attaching to application ..."); + VirtualMachine vm = VirtualMachine.attach(pid); + + System.out.println(" - Test: system properties in target VM"); + Properties props = vm.getSystemProperties(); + String value = props.getProperty("attach.test"); + if (value == null || !value.equals("true")) { + throw new RuntimeException("attach.test property not set"); + } + System.out.println(" - attach.test property set as expected"); + } + } + + private static void runNoExistTest(Path tmpDir) throws Throwable { + // Arguments : [-XX:AltTempDir=] -version + String[] args = new String[] { "-XX:AltTempDir=" + tmpDir, "-version" }; + OutputAnalyzer output = ProcessTools.executeTestJava(args); + output.shouldMatch("\\[warning\\]\\[os *\\] Warning: AltTempDir is not an existing or writable directory"); + // Still passes, it's just a warning. + output.shouldHaveExitValue(0); + } + + private static void runRelativeTest(Path tmpDir) throws Throwable { + // Arguments : [-XX:AltTempDir=] -version + String[] args = new String[] { "-XX:AltTempDir=" + tmpDir, "-version" }; + OutputAnalyzer output = ProcessTools.executeTestJava(args); + output.shouldMatch("\\[warning\\]\\[os *\\] Warning: AltTempDir is ignored because it must be an absolute pathname"); + // Still passes, it's just a warning. + output.shouldHaveExitValue(0); + } +} diff --git a/test/jdk/com/sun/tools/attach/TempDirTest.java b/test/jdk/com/sun/tools/attach/TempDirTest.java index e0552d15fce..14b65207da9 100644 --- a/test/jdk/com/sun/tools/attach/TempDirTest.java +++ b/test/jdk/com/sun/tools/attach/TempDirTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -64,7 +64,9 @@ public class TempDirTest { Path targetTmpDir = Files.createTempDirectory("TempDirTest-target"); targetTmpDir.toFile().deleteOnExit(); - // run the test with all possible combinations of setting java.io.tmpdir + // Run the test with all possible combinations of setting java.io.tmpdir. + // Note that the attach mechanism doesn't really use java.io.tmpdir, but this test verifies + // that different java.io.tmpdir settings for client and target don't break the attach mechanism. runExperiment(null, null); runExperiment(clientTmpDir, null); runExperiment(clientTmpDir, targetTmpDir); diff --git a/test/jdk/java/awt/Frame/PackTwiceTest.java b/test/jdk/java/awt/Frame/PackTwiceTest.java index 63cd20612f0..ee948665d15 100644 --- a/test/jdk/java/awt/Frame/PackTwiceTest.java +++ b/test/jdk/java/awt/Frame/PackTwiceTest.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 @@ -36,7 +36,7 @@ import java.awt.TextField; public class PackTwiceTest { public static void main(String[] args) throws Exception { String INSTRUCTIONS = """ - 1. You would see a Frame titled 'TestFrame' + 1. You would see a Frame titled 'PackTwiceTest TestFrame' 2. The Frame displays a text as below: 'I am a lengthy sentence...can you see me?' 3. If you can see the full text without resizing the frame diff --git a/test/jdk/java/awt/TextArea/TextAreaCRLFAutoDetectManualTest.java b/test/jdk/java/awt/TextArea/TextAreaCRLFAutoDetectManualTest.java index c8c3f0662a5..65968f4fe28 100644 --- a/test/jdk/java/awt/TextArea/TextAreaCRLFAutoDetectManualTest.java +++ b/test/jdk/java/awt/TextArea/TextAreaCRLFAutoDetectManualTest.java @@ -33,7 +33,8 @@ import java.awt.event.ActionListener; /* * @test - * @bug 4800187 + * @bug 4800187 8384287 + * @requires (os.family == "windows") * @summary REGRESSION:show the wrong selection when there are \r characters in the text * @library /java/awt/regtesthelpers * @build PassFailJFrame diff --git a/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java b/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java index 8d132825de0..b4505bbda97 100644 --- a/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java +++ b/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.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 @@ -63,7 +63,7 @@ public class LWButton extends LWComponent { private transient ActionListener actionListener; /* - * The action to be performaed once a button has been + * The action to be performed once a button has been * pressed. * actionCommand can be null. * @serial diff --git a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java index 13a88e8c590..aa9d8ff4156 100644 --- a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java +++ b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8255800 8369129 8376297 + * @bug 8255800 8369129 8376297 8386671 * @summary verify Raster + SampleModel creation vs spec. */ @@ -931,9 +931,10 @@ public class CreateRasterExceptionTest { /* @throws ArrayIndexOutOfBoundsException if any element of {@code bankIndices} * is greater or equal to the number of bands in {@code dataBuffer} */ + DataBuffer dBuffer2Bands = new DataBufferByte(15, 2); int[] indices = new int[] { 0, 1, 2 }; int[] offsets = new int[] { 0, 0, 0 }; - Raster.createBandedRaster(dBuffer, 1, 1, 1, + Raster.createBandedRaster(dBuffer2Bands, 1, 1, 1, indices, offsets, null); noException(); } catch (ArrayIndexOutOfBoundsException t) { @@ -1198,6 +1199,21 @@ public class CreateRasterExceptionTest { "Got expected exception for bad databuffer type"); System.out.println(t); } + + try { + /* @throws IllegalArgumentException if any element of {@code bandOffsets} is greater + * than {@code pixelStride} or the {@code scanlineStride} + */ + int[] offsets = new int[] {2}; + Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, + 1, 1, 1, 1, offsets, null); + noException(); + } catch (IllegalArgumentException t) { + System.out.println( + "Got expected exception for element too large"); + System.out.println(t); + } + } /* createInterleavedRaster(DataBuffer dBuffer, @@ -1344,7 +1360,7 @@ public class CreateRasterExceptionTest { /* @throws IllegalArgumentException if any element of {@code bandOffsets} is greater * than {@code pixelStride} or the {@code scanlineStride} */ - int[] offsets = new int[] { 0, 1, 2}; + int[] offsets = new int[] {2}; Raster.createInterleavedRaster(dBuffer, 1, 1, 1, 1, offsets, null); noException(); diff --git a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java index 0ac7ea474a4..3522921bdd3 100644 --- a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java +++ b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.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 @@ -29,7 +29,6 @@ * @requires jdk.foreign.linker != "UNSUPPORTED" * @requires !vm.musl * - * @enablePreview * @build TestEnableNativeAccessJarManifest * panama_module/* * org.openjdk.foreigntest.unnamed.PanamaMainUnnamedModule diff --git a/test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java b/test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java new file mode 100644 index 00000000000..acca0d095c3 --- /dev/null +++ b/test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java @@ -0,0 +1,93 @@ +/* + * 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 + * @run testng TestNormalizeBooleanVarHandle + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import static java.lang.foreign.ValueLayout.*; +import static org.testng.Assert.*; + +// test normalization of smaller than int primitive types +public class TestNormalizeBooleanVarHandle { + + static final VarHandle VH = JAVA_BOOLEAN.varHandle(); + + @Test(dataProvider = "bools") + public void testBool(Function segmentFactory, Predicate accessor, + byte testValue, boolean expected) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment ms = segmentFactory.apply(arena); + ms.set(JAVA_BYTE, 0L, testValue); + + boolean b = accessor.test(ms); + assertEquals(b, expected); + } + } + + @DataProvider + public static Object[][] bools() { + List cases = new ArrayList<>(); + for (Function segmentFactory : factories()) { + for (Predicate accessor : accessors()) { + cases.add(new Object[]{ segmentFactory, accessor, + (byte) 0b0 , false }); // canonical false + cases.add(new Object[]{ segmentFactory, accessor, + (byte) 0b01, true }); // canonical true + cases.add(new Object[]{ segmentFactory, accessor, + (byte) 0b10, true }); // zero least significant bit, but non-zero first byte + } + } + + return cases.toArray(Object[][]::new); + } + + private static List> factories() { + return List.of( + a -> a.allocate(JAVA_BYTE), + _ -> MemorySegment.ofArray(new byte[1]) + ); + } + + private static List> accessors() { + return List.of( + ms -> ms.get(JAVA_BOOLEAN, 0L), + ms -> (boolean) VH.get(ms, 0L), + ms -> (boolean) VH.getVolatile(ms, 0L), + ms -> (boolean) VH.getAcquire(ms, 0L), + ms -> (boolean) VH.getOpaque(ms, 0L) + ); + } +} diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java index cdaa94e8289..6cb71022c0c 100644 --- a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -26,6 +26,7 @@ * @bug 8253525 * @summary Test for fInst.getObjectSize with 32-bit compressed oops * @library /test/lib + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -54,6 +55,7 @@ * @summary Test for fInst.getObjectSize with zero-based compressed oops * @library /test/lib * @requires vm.bits == 64 + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -82,6 +84,7 @@ * @summary Test for fInst.getObjectSize without compressed oops * @library /test/lib * @requires vm.bits == 64 + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -110,6 +113,7 @@ * @summary Test for fInst.getObjectSize with 32-bit compressed oops * @library /test/lib * @requires vm.debug + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -142,6 +146,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -174,6 +179,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -206,6 +212,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -238,6 +245,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -271,6 +279,7 @@ * @requires vm.bits == 64 * @requires vm.debug * @requires os.maxMemory >= 10G + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest diff --git a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh index f2412a92a43..af2f3f8b3f1 100644 --- a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh +++ b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2020, 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 @@ -52,7 +52,7 @@ case "$OS" in OS="Windows" FS="\\" ;; - CYGWIN* | MSYS* | MINGW*) + CYGWIN*) PS=";" OS="Windows" FS="\\" diff --git a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java index d7ca5912273..56be7008f41 100644 --- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java +++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java @@ -79,6 +79,7 @@ public class ObjectMethodsTest { } static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + static final MethodHandles.Lookup UNPRIVILEGED_LOOKUP = LOOKUP.dropLookupMode(MethodHandles.Lookup.PRIVATE); @Test public void testEqualsC() throws Throwable { @@ -184,6 +185,9 @@ public class ObjectMethodsTest { assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS)); assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS)); + // Unprivileged lookup + assertThrows(IAE, () -> ObjectMethods.bootstrap(UNPRIVILEGED_LOOKUP, name, type, C.class, "x;y", C.ACCESSORS)); + // Bad indy call receiver type - change C to this test class assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS)); diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index 061ce2ae241..76bfe7616f6 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -42,40 +42,25 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8318144 * @enablePreview * @compile SwitchBootstrapsTest.java - * @run junit/othervm SwitchBootstrapsTest + * @run junit SwitchBootstrapsTest */ public class SwitchBootstrapsTest { - public static final MethodHandle BSM_TYPE_SWITCH; - public static final MethodHandle BSM_ENUM_SWITCH; - - static { - try { - BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch", - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); - BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch", - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); - } - catch (ReflectiveOperationException e) { - throw new AssertionError("Should not happen", e); - } - } - private void testType(Object target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } private void testPrimitiveType(Object target, Class targetType, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetType, int.class); - MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); } @@ -85,7 +70,7 @@ public class SwitchBootstrapsTest { private void testEnum(Class targetClass, Enum target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetClass, int.class); - MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } @@ -188,7 +173,7 @@ public class SwitchBootstrapsTest { //null invocation name: MethodType switchType = MethodType.methodType(int.class, E1.class, int.class); - MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), null, switchType).dynamicInvoker(); assertEquals(0, (int) indy.invoke(E1.A, 0)); } @@ -229,7 +214,7 @@ public class SwitchBootstrapsTest { }; for (MethodType switchType : switchTypes) { assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType) ); } MethodType[] enumSwitchTypes = new MethodType[] { @@ -240,7 +225,7 @@ public class SwitchBootstrapsTest { }; for (MethodType enumSwitchType : enumSwitchTypes) { assertThrows(IllegalArgumentException.class, () -> - BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType) + SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType) ); } } @@ -270,23 +255,23 @@ public class SwitchBootstrapsTest { public void testNullLabels() throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); assertThrows(NullPointerException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, (Object[]) null) ); assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, new Object[] {1, null, String.class}) ); MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class); assertThrows(NullPointerException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) ); assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, new Object[] {1, null, String.class}) ); //null invocationName is OK: - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType, - new Object[] {Object.class}); + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), null, switchType, + Object.class); } private static AtomicBoolean enumInitialized = new AtomicBoolean(); @@ -304,7 +289,7 @@ public class SwitchBootstrapsTest { MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class); - CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"}); + CallSite invocation = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType, "A"); assertFalse(enumInitialized.get()); assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); @@ -330,7 +315,7 @@ public class SwitchBootstrapsTest { EnumDesc.of(ClassDesc.of(E.class.getName()), "A"), "test" }; - CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels); + CallSite invocation = (CallSite) SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels); assertFalse(enumInitialized.get()); assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); @@ -402,21 +387,37 @@ public class SwitchBootstrapsTest { } @Test - public void testNullLookup() throws Throwable { + public void testNullLookup() { assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - BSM_TYPE_SWITCH.invoke(null, "", switchType, Object.class); + SwitchBootstraps.typeSwitch(null, "", switchType, Object.class); }); enum E {} assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); - BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {}); + SwitchBootstraps.enumSwitch(null, "", switchType); }); assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); - BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {"A"}); + SwitchBootstraps.enumSwitch(null, "", switchType, "A"); + }); + } + + @Test + public void testUnprivilegedLookup() { + var lookup = MethodHandles.lookup().dropLookupMode(MethodHandles.Lookup.PRIVATE); + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); + SwitchBootstraps.typeSwitch(lookup, "", switchType, Object.class); + }); + enum E {} + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, E.class, int.class); + SwitchBootstraps.enumSwitch(lookup, "", switchType); + }); + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, E.class, int.class); + SwitchBootstraps.enumSwitch(lookup, "", switchType, "A"); }); } } diff --git a/test/jdk/java/net/CookieHandler/B6791927.java b/test/jdk/java/net/CookieHandler/B6791927.java index bc5374b2a98..ceeff260665 100644 --- a/test/jdk/java/net/CookieHandler/B6791927.java +++ b/test/jdk/java/net/CookieHandler/B6791927.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, 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 @@ -24,7 +24,7 @@ /** * @test * @bug 6791927 8233886 - * @summary Wrong Locale in HttpCookie::expiryDate2DeltaSeconds + * @summary Wrong Locale in HttpCookie::parseExpires * @run main/othervm B6791927 */ diff --git a/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java b/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java index 5cf7208d2ca..e2769d8dd61 100644 --- a/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java +++ b/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8000525 + * @bug 8000525 8380549 * @library /test/lib */ @@ -33,6 +33,8 @@ import java.io.*; import java.text.*; import jdk.test.lib.net.URIBuilder; +import static jdk.test.lib.Asserts.assertEquals; + public class ExpiredCookieTest { // lifted from HttpCookie.java private final static String[] COOKIE_DATE_FORMATS = { @@ -92,15 +94,28 @@ public class ExpiredCookieTest { cm.put(uri, header); CookieStore cookieJar = cm.getCookieStore(); - List cookies = cookieJar.getCookies(); + Set names = new TreeSet<>(); + for (HttpCookie cookie : cookieJar.getCookies()) + names.add(cookie.getName()); + + Set expected; if (COOKIE_DATE_FORMATS[i].contains("yyyy")) { - if (cookies.size() != 2) - throw new RuntimeException( - "Incorrectly parsing a bad date"); - } else if (cookies.size() != 1) { - throw new RuntimeException( - "Incorrectly parsing a bad date"); + // Four-digit years parse unambiguously: TEST1 and TEST2 are + // in the past and expire, while TEST3 and TEST4 remain. + expected = new TreeSet<>(List.of("TEST3", "TEST4")); + } else { + // Two-digit years make TEST2 and TEST3 resolve to a mismatched + // day-of-week, so strict parsing rejects the Expires value; per + // RFC 6265 section 5.2.1 an unparseable Expires is ignored, so + // they remain as session cookies. TEST1 parses cleanly but is + // already expired, so it is dropped. TEST4's two-digit year + // round-trips to itself (69 -> 2069), so it parses and remains + // because its expiry is still in the future. + expected = new TreeSet<>(List.of("TEST2", "TEST3", "TEST4")); } + assertEquals(expected, names, + "Incorrectly parsing a bad date, format: " + + COOKIE_DATE_FORMATS[i]); } } } diff --git a/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java b/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java index 6704a290836..79139d69e50 100644 --- a/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java +++ b/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java @@ -33,6 +33,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class MaxAgeExpires { @@ -138,4 +139,26 @@ public class MaxAgeExpires { cookie.setMaxAge(-2); assertEquals(-2, cookie.getMaxAge()); } + + public static Object[][] unparseableDates() { + return new Object[][] { + { "GARBAGE" }, + { "2024-01-01T00:00:00Z" }, // format not supported by RFC-6265 + { "January 1, 2099 00:00:00 GMT" } // format not supported by RFC-6265 + }; + } + + @ParameterizedTest + @MethodSource("unparseableDates") + public void testUnparseableExpires(String badDate) { + // RFC 6265 section 5.2.1: if the expires value fails to parse, + // the cookie-av should be ignored. + // That results in the HttpCookie implementation to have maxAge value of -1. + HttpCookie cookie = HttpCookie.parse( + "Set-Cookie: name=value; expires=" + badDate).get(0); + assertEquals(-1, cookie.getMaxAge(), + "Unparseable expires=\"" + badDate + "\" should be ignored"); + assertFalse(cookie.hasExpired(), + "Cookie with ignored expires should not be expired"); + } } diff --git a/test/jdk/java/net/httpclient/CancelRequestTest.java b/test/jdk/java/net/httpclient/CancelRequestTest.java index f21d13d5e98..418127c7735 100644 --- a/test/jdk/java/net/httpclient/CancelRequestTest.java +++ b/test/jdk/java/net/httpclient/CancelRequestTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8245462 8229822 8254786 8297075 8297149 8298340 8302635 8377181 + * @bug 8245462 8229822 8254786 8297075 8297149 8298340 8302635 8377181 8380967 * @summary Tests cancelling the request. * @library /test/lib /test/jdk/java/net/httpclient/lib * @key randomness @@ -79,6 +79,7 @@ import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Assumptions; @@ -394,15 +395,9 @@ public class CancelRequestTest implements HttpServerAdapters { requestLatch.countDown(); } - // Cancelling the request may cause an IOException instead... - boolean hasCancellationException = false; - try { - cf1.get(); - } catch (CancellationException | ExecutionException x) { - out.println(now() + "Got expected exception: " + x); - assertTrue(isCancelled(x)); - hasCancellationException = x instanceof CancellationException; - } + var cancelX = assertThrows(CancellationException.class, cf1::get); + out.println(now() + "Got expected exception: " + cancelX); + assertTrue(cf1.isCancelled()); // because it's cf1 that was cancelled then response might not have // completed yet - so wait for it here... @@ -447,7 +442,6 @@ public class CancelRequestTest implements HttpServerAdapters { assertTrue(response.isDone()); assertFalse(response.isCancelled()); - assertEquals(hasCancellationException, cf1.isCancelled()); assertTrue(cf2.isDone()); assertFalse(cf2.isCancelled()); assertEquals(0, latch.getCount()); @@ -529,15 +523,9 @@ public class CancelRequestTest implements HttpServerAdapters { requestLatch.countDown(); } - // Cancelling the request may cause an IOException instead... - boolean hasCancellationException = false; - try { - cf1.get(); - } catch (CancellationException | ExecutionException x) { - out.println(now() + "Got expected exception: " + x); - assertTrue(isCancelled(x)); - hasCancellationException = x instanceof CancellationException; - } + var cancelX = assertThrows(CancellationException.class, cf1::get); + out.println(now() + "Got expected exception: " + cancelX); + assertTrue(cf1.isCancelled()); // because it's cf1 that was cancelled then response might not have // completed yet - so wait for it here... @@ -576,7 +564,6 @@ public class CancelRequestTest implements HttpServerAdapters { assertTrue(response.isDone()); assertFalse(response.isCancelled()); - assertEquals(hasCancellationException, cf1.isCancelled()); assertTrue(cf2.isDone()); assertFalse(cf2.isCancelled()); assertEquals(0, latch.getCount()); diff --git a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java index c38671e65b8..ff8e3804996 100644 --- a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java +++ b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java @@ -77,7 +77,7 @@ */ /* * @test id=useNioSelector - * @bug 8087112 8372409 + * @bug 8087112 8372409 8386989 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer diff --git a/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java b/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java index 0a363e104ae..3a33bbcf95d 100644 --- a/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java +++ b/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java @@ -80,16 +80,19 @@ import javax.net.ssl.SSLSession; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; /* * @test + * @bug 8349910 8386985 * @summary tests the logic to build an AckFrame * @library /test/lib * @library ../debug @@ -102,6 +105,7 @@ import org.junit.jupiter.params.provider.MethodSource; * @run junit/othervm -Dseed=-4159871071396382784 ${test.main.class} * @run junit/othervm -Dseed=2252276218459363615 ${test.main.class} * @run junit/othervm -Dseed=-5130588140709404919 ${test.main.class} + * @run junit/othervm -Dseed=4257295716830862528 ${test.main.class} */ // -Djdk.internal.httpclient.debug=true public class PacketSpaceManagerTest { @@ -766,6 +770,26 @@ public class PacketSpaceManagerTest { } } + // Sends a trivial INITIAL packet, with a CRYPTO frame containing + // a payload of length 1 for the provided offset. If ackFrameToSend + // is not null it will be included in the packet. + public List sendPacket(long offset, AckFrame ackFrameToSend, Packet packet, long largestReceivedAckedPN) { + // add a crypto frame and build the packet + CryptoFrame crypto = new CryptoFrame(offset, 1, + ByteBuffer.wrap(new byte[] {nextByte(offset)})); + List frames = ackFrameToSend == null ? + List.of(crypto) : List.of(crypto, ackFrameToSend); + QuicPacket newPacket = codingContext.encoder + .newInitialPacket(localId, peerId, + null, + packet.packetNumber, + largestReceivedAckedPN, + frames, codingContext); + // pretend that we sent a packet + manager.packetSent(newPacket, -1, packet.packetNumber); + return frames; + } + /** * Drives the test by pretending to emit each packet in order, * then pretending to receive ack frames (as soon as possible @@ -830,19 +854,8 @@ public class PacketSpaceManagerTest { debug.log("largestAckSent is: " + largestAckAcked); } - // add a crypto frame and build the packet - CryptoFrame crypto = new CryptoFrame(offset, 1, - ByteBuffer.wrap(new byte[] {nextByte(offset)})); - List frames = ackFrameToSend == null ? - List.of(crypto) : List.of(crypto, ackFrameToSend); - QuicPacket newPacket = codingContext.encoder - .newInitialPacket(localId, peerId, - null, - packet.packetNumber, - largestReceivedAckedPN, - frames, codingContext); - // pretend that we sent a packet - manager.packetSent(newPacket, -1, packet.packetNumber); + // send a packet + List frames = sendPacket(offset, ackFrameToSend, packet, largestReceivedAckedPN); // compute next deadline var nextDeadline = timerQueue.nextDeadline(); @@ -1125,4 +1138,53 @@ public class PacketSpaceManagerTest { driver.check(); } + @Test + public void testPacketSent() throws Exception { + // this test case is specifically for JDK-8386985 + System.out.printf("%n ------- testPacketSent ------- %n"); + + // create a minimal SynchronousTestDriver + TestCase testCase = new TestCase(List.of(new Acknowledged(1, 3), new Acknowledged(4,4)), + List.of(new Packet(3, 0), new Packet(4, 0))); + SynchronousTestDriver driver = new SynchronousTestDriver(testCase); + + // send a first ack-eliciting packet, and move the timeline past PTO + driver.sendPacket(0, null, new Packet(1, 0), -1); + Deadline pto = driver.manager.nextScheduledDeadline(); // should be PTO + driver.timeSource.advance(driver.timeSource.instant().until(pto, ChronoUnit.MILLIS) + 250, ChronoUnit.MILLIS); + + // start processing events, but delay the task that will run the transmitter + ArrayList tasks = new ArrayList<>(); + Executor executor = new Executor() { + @Override + public void execute(Runnable command) { + tasks.add(command); + } + }; + driver.timerQueue.processEventsAndReturnNextDeadline(driver.timeSource.instant(), executor); + + // acknowledge the first packet so that it's no longer pending retransmission + driver.manager.processAckFrame(new AckFrameBuilder().addAck(1).build()); + + // send a second packet, and examine the timerQueue next deadline + // if sending the second packet didn't cause the task to be rescheduled, we + // will observe Deadline.MAX, or a deadline before now: that's the bug. + driver.sendPacket(1, null, new Packet(2, 0), -1); + + Deadline next = driver.timerQueue.nextDeadline(); + assertNotEquals(next, Deadline.MAX); + assertTrue(next.isAfter(driver.timeSource.instant())); + + // now finish running the task and ack the second packet, + // so that we leave the packet space manager in a clean state + // for running the driver with the next two packets. + for (Runnable task : tasks) { + task.run(); + } + driver.manager.processAckFrame(new AckFrameBuilder() + .addAck(1).addAck(2).build()); + driver.run(); + driver.check(); + } + } diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java index 2c33f6f0018..ccac8fcf439 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java @@ -27,10 +27,16 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class MinimalFutureTest { @@ -101,6 +107,32 @@ public class MinimalFutureTest { } } + @Test + public void testCancel() { + AtomicInteger cancelCount = new AtomicInteger(); + AtomicBoolean cancelled = new AtomicBoolean(); + Cancelable cancelable = mayInterruptIfRunning -> { + cancelCount.incrementAndGet(); + if (mayInterruptIfRunning) { + cancelled.set(true); + } + return cancelled.get(); + }; + MinimalFuture future = new MinimalFuture<>(cancelable); + CompletableFuture dependent = future.copy().whenComplete((x,t) -> + System.out.println("expected: " + t)); + assertTrue(dependent.cancel(false)); + assertTrue(dependent.isCancelled()); + assertFalse(future.isCancelled()); + assertFalse(cancelled.get()); + assertEquals(1, cancelCount.get()); + assertTrue(dependent.cancel(true)); + assertTrue(dependent.isCancelled()); + assertFalse(future.isCancelled()); + assertTrue(cancelled.get()); + assertEquals(2, cancelCount.get()); + } + private static CompletableFuture otherFuture() { return MinimalFuture.completedFuture(new Object()); } diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 870a84a8927..e481f6f1594 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -212,6 +212,18 @@ public class SecureDS { Path link = createSymbolicLink(aDir.resolve(linkEntry), fileEntry); Set permsLink = getPosixFilePermissions(link, NOFOLLOW_LINKS); + // Test setting permissions on a regular file through the no-follow view + view = stream.getFileAttributeView(fileEntry, PosixFileAttributeView.class, NOFOLLOW_LINKS); + view.setPermissions(noperms); + assertEquals(noperms, getPosixFilePermissions(file)); + try { + view.setPermissions(permsFile); + assertEquals(permsFile, getPosixFilePermissions(file)); + } catch (AccessDeniedException e) { + // Fails on older Linux systems without fchmodat AT_SYMLINK_NOFOLLOW support + setPosixFilePermissions(file, permsFile); + } + // Test following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class); view.setPermissions(noperms); @@ -220,14 +232,19 @@ public class SecureDS { view.setPermissions(permsFile); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - // Symbolic link permissions do not apply on Linux - if (!Platform.isLinux()) { - // Test not following link to file - view = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); - view.setPermissions(noperms); + + // Test not following link to file + var linkView = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); + if (Platform.isLinux()) { + // Symbolic link permissions do not apply on Linux + assertThrows(IOException.class, () -> linkView.setPermissions(noperms)); + assertEquals(permsFile, getPosixFilePermissions(file)); + assertThrows(IOException.class, () -> linkView.setPermissions(permsLink)); + } else { + linkView.setPermissions(noperms); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(noperms, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - view.setPermissions(permsLink); + linkView.setPermissions(permsLink); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); } diff --git a/test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java b/test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..516191cfa01 --- /dev/null +++ b/test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java @@ -0,0 +1,259 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for KeyStore + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms KEYSTORE.JKs true + * @run main/othervm TestLegacyAlgorithms keySTORE.what false + * @run main/othervm TestLegacyAlgorithms kEYstoRe.jceKS false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=KEYSTORE.JKS + * -Djdk.crypto.disabledAlgorithms=KEYSTORE.JKS + * TestLegacyAlgorithms KEYSTORE.JKS false true + */ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Provider; +import java.security.Security; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final String DIR = System.getProperty("test.src", "."); + private static final char[] PASSWD = "passphrase".toCharArray(); + private static final String JKS_FN = "keystore.jks"; + + private static final List ALG_LIST = + List.of("JKS", "jkS"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated KeyStore algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for KeyStore " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for KeyStore " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated KeyStore algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for KeyStore: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for KeyStore: " + warnS); + } + + private static void checkWarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + // Disable the algorithm and check that a warning is not emitted. + private static void warnDisabledTest() + throws Exception { + File jksFile = new File(DIR, JKS_FN); + checkWarn("no warning when the algorithm is disabled", + "JKS", false, () -> { + Utils.runAndCheckException( + () -> KeyStore.getInstance("JKS"), + KeyStoreException.class); + Utils.runAndCheckException( + () -> KeyStore.getInstance(jksFile, PASSWD), + KeyStoreException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkWarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultKS.run(a)); + } + + File jksFile = new File(DIR, JKS_FN); + + checkWarn("file with password: " + jksFile, "JKS", shouldWarn, + () -> PasswordKS.run(jksFile)); + + checkWarn("file with LoadStoreParameter: " + jksFile, "JKS", + shouldWarn, () -> LoadStoreParamKS.run(jksFile)); + + Provider[] providers = Security.getProviders("KeyStore.JKS"); + if (providers.length > 0) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + Provider p = providers[0]; + for (String a : ALG_LIST) { + checkWarn("provider object " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvObjKS.run(a, p)); + + checkWarn("provider name " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvNameKS.run(a, p)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultKS { + static void run(String alg) throws Exception { + KeyStore k = KeyStore.getInstance(alg); + System.out.println(" type lookup: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(alg); + System.out.println(" type lookup again: got KeyStore w/ alg " + + k.getType()); + } + } + + private static final class PasswordKS { + static void run(File jksFile) throws Exception { + KeyStore k = KeyStore.getInstance(jksFile, PASSWD); + System.out.println(" file+password: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(jksFile, PASSWD); + System.out.println(" file+password again: got KeyStore " + + "w/ alg " + k.getType()); + } + } + + private static final class LoadStoreParamKS { + static void run(File jksFile) throws Exception { + KeyStore k = KeyStore.getInstance(jksFile, + () -> new KeyStore.PasswordProtection(PASSWD)); + System.out.println(" file+LoadStoreParameter: got KeyStore " + + "w/ alg " + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(jksFile, + () -> new KeyStore.PasswordProtection(PASSWD)); + System.out.println(" file+LoadStoreParameter again: got " + + "KeyStore w/ alg " + k.getType()); + } + } + + private static final class ProvObjKS { + static void run(String alg, Provider provider) throws Exception { + KeyStore k = KeyStore.getInstance(alg, provider); + System.out.println(" provider object: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(alg, provider); + System.out.println(" provider object again: got KeyStore " + + "w/ alg " + k.getType()); + } + } + + private static final class ProvNameKS { + static void run(String alg, Provider provider) throws Exception { + KeyStore k = KeyStore.getInstance(alg, provider.getName()); + System.out.println(" provider name: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got KeyStore " + + "w/ alg " + k.getType()); + } + } +} diff --git a/test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java b/test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..61275b03f5d --- /dev/null +++ b/test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java @@ -0,0 +1,212 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for MessageDigest + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms MESSAGEdigest.Sha-512 true + * @run main/othervm TestLegacyAlgorithms messageDIGest.what false + * @run main/othervm TestLegacyAlgorithms meSSagedIgest.sHA-512/224 false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=MESSAGEdigest.Sha-512 + * -Djdk.crypto.disabledAlgorithms=MESSAGEdigest.Sha-512 + * TestLegacyAlgorithms MESSAGEdigest.Sha-512 false true + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final List ALG_LIST = + List.of("sHA-512", "shA-512", "2.16.840.1.101.3.4.2.3"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated MessageDigest algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for MessageDigest " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for MessageDigest " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated MessageDigest algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for MessageDigest: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for MessageDigest: " + warnS); + } + + private static void checkwarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + // Disable the algorithm and check that a warning is not emitted. + private static void warnDisabledTest() + throws Exception { + checkwarn("no warning when the algorithm is disabled", + "SHA-512", false, () -> { + Utils.runAndCheckException( + () -> MessageDigest.getInstance("SHA-512"), + NoSuchAlgorithmException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkwarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultMD.run(a)); + } + + Provider[] providers = Security.getProviders("MessageDigest.SHA-512"); + if (providers.length > 0) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + Provider p = providers[0]; + for (String a : ALG_LIST) { + checkwarn("provider object " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvObjMD.run(a, p)); + + checkwarn("provider name " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvNameMD.run(a, p)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultMD { + static void run(String alg) throws Exception { + MessageDigest m = MessageDigest.getInstance(alg); + System.out.println(" type lookup: got MessageDigest w/ alg " + + m.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + m = MessageDigest.getInstance(alg); + System.out.println(" type lookup again: got MessageDigest w/ alg " + + m.getAlgorithm()); + } + } + + private static final class ProvObjMD { + static void run(String alg, Provider provider) throws Exception { + MessageDigest m = MessageDigest.getInstance(alg, provider); + System.out.println(" provider object: got MessageDigest w/ alg " + + m.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + m = MessageDigest.getInstance(alg, provider); + System.out.println(" provider object again: got MessageDigest " + + "w/ alg " + m.getAlgorithm()); + } + } + + private static final class ProvNameMD { + static void run(String alg, Provider provider) throws Exception { + MessageDigest m = MessageDigest.getInstance(alg, provider.getName()); + System.out.println(" provider name: got MessageDigest w/ alg " + + m.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + m = MessageDigest.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got MessageDigest " + + "w/ alg " + m.getAlgorithm()); + } + } +} diff --git a/test/jdk/java/security/Provider/SupportsParameter.java b/test/jdk/java/security/Provider/SupportsParameter.java index 039fb3d0797..3325ad9680a 100644 --- a/test/jdk/java/security/Provider/SupportsParameter.java +++ b/test/jdk/java/security/Provider/SupportsParameter.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 @@ -23,7 +23,7 @@ /** * @test - * @bug 4911081 8130181 + * @bug 4911081 8130181 8358549 * @library /test/lib * @summary verify that Provider.Service.supportsParameter() works * @author Andreas Sterbenz @@ -112,7 +112,9 @@ public class SupportsParameter { put("Signature.DSA0", "foo.DSA0"); put("Signature.DSA", "foo.DSA"); - put("Signature.DSA SupportedKeyClasses", + // Extra spaces between "Signature.DSA" and "SupportedKeyClasses" + // are used to verify that whitespace is trimmed. + put("Signature.DSA SupportedKeyClasses", "java.security.interfaces.DSAPublicKey" + "|java.security.interfaces.DSAPrivateKey"); diff --git a/test/jdk/java/security/Signature/TestLegacyAlgorithms.java b/test/jdk/java/security/Signature/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..bf745fba3e1 --- /dev/null +++ b/test/jdk/java/security/Signature/TestLegacyAlgorithms.java @@ -0,0 +1,212 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for Signature + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms SIGNATURe.sha512withRSA true + * @run main/othervm TestLegacyAlgorithms signaturE.what false + * @run main/othervm TestLegacyAlgorithms SiGnAtUrE.SHa512/224withRSA false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=SIGNATURe.sha512withRSA + * -Djdk.crypto.disabledAlgorithms=SIGNATURe.sha512withRSA + * TestLegacyAlgorithms SIGNATURe.sha512withRSA false true + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.Signature; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final List ALG_LIST = + List.of("sha512withRsa", "1.2.840.113549.1.1.13"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated Signature algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for Signature " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for Signature " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated Signature algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for Signature: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for Signature: " + warnS); + } + + private static void checkWarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + // Disable the algorithm and check that a warning is not emitted. + private static void warnDisabledTest() + throws Exception { + checkWarn("no warning when the algorithm is disabled", + "sha512withRSA", false, () -> { + Utils.runAndCheckException( + () -> Signature.getInstance("sha512withRSA"), + NoSuchAlgorithmException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkWarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultSig.run(a)); + } + + Provider[] providers = Security.getProviders("Signature.SHA512withRSA"); + if (providers.length > 0) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + Provider p = providers[0]; + for (String a : ALG_LIST) { + checkWarn("provider object " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvObjSig.run(a, p)); + + checkWarn("provider name " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvNameSig.run(a, p)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultSig { + static void run(String alg) throws Exception { + Signature s = Signature.getInstance(alg); + System.out.println(" type lookup: got Signature w/ alg " + + s.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + s = Signature.getInstance(alg); + System.out.println(" type lookup again: got Signature w/ alg " + + s.getAlgorithm()); + } + } + + private static final class ProvObjSig { + static void run(String alg, Provider provider) throws Exception { + Signature s = Signature.getInstance(alg, provider); + System.out.println(" provider object: got Signature w/ alg " + + s.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + s = Signature.getInstance(alg, provider); + System.out.println(" provider object again: got Signature " + + "w/ alg " + s.getAlgorithm()); + } + } + + private static final class ProvNameSig { + static void run(String alg, Provider provider) throws Exception { + Signature s = Signature.getInstance(alg, provider.getName()); + System.out.println(" provider name: got Signature w/ alg " + + s.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + s = Signature.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got Signature " + + "w/ alg " + s.getAlgorithm()); + } + } +} diff --git a/test/jdk/java/util/Locale/LocaleEnhanceTest.java b/test/jdk/java/util/Locale/LocaleEnhanceTest.java index 8bcbe20d197..a4c056d3a77 100644 --- a/test/jdk/java/util/Locale/LocaleEnhanceTest.java +++ b/test/jdk/java/util/Locale/LocaleEnhanceTest.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 @@ -44,6 +44,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -57,7 +58,7 @@ import static org.junit.jupiter.api.Assertions.fail; * @test * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603 * 7044019 8008577 8176853 8255086 8263202 8287868 8174269 8369452 - * 8369590 + * 8369590 8387185 8387253 8387455 * @summary test API changes to Locale * @modules jdk.localedata * @run junit/othervm -esa LocaleEnhanceTest @@ -497,6 +498,23 @@ public class LocaleEnhanceTest { // private use only language tag is preserved (no extra "und") {"x-elmer", "x-elmer"}, {"x-lvariant-JP", "x-lvariant-JP"}, + // Legacy locale cases + // no/NO/NY case is normalized during `toLanguageTag` + // ja/JP/JP & th/TH/TH case is normalized during `forLanguageTag` + // Script prevents the legacy conversions + {"no-Latn-NO-x-lvariant-NY", + "no-Latn-NO-x-lvariant-NY"}, + {"ja-Jpan-JP-x-lvariant-JP", + "ja-Jpan-JP-x-lvariant-JP"}, + {"th-Thai-TH-x-lvariant-TH", + "th-Thai-TH-x-lvariant-TH"}, + // Unexpected extensions prevent the legacy conversions + {"no-NO-a-foo-x-lvariant-NY", + "no-NO-a-foo-x-lvariant-NY"}, + {"ja-JP-a-foo-x-lvariant-JP", + "ja-JP-a-foo-x-lvariant-JP"}, + {"th-TH-a-foo-x-lvariant-TH", + "th-TH-a-foo-x-lvariant-TH"}, }; for (String[] test : tests1) { Locale locale = Locale.forLanguageTag(test[0]); @@ -728,6 +746,34 @@ public class LocaleEnhanceTest { assertEquals("nn", locale.getLanguage(), "no_NO_NY language"); assertEquals("", locale.getVariant(), "no_NO_NY variant"); + // Legacy locales that stripped their compatibility extensions are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.of("ja", "JP", "JP").stripExtensions())); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.of("th", "TH", "TH").stripExtensions())); + + // Legacy locales without the correct Unicode locale extension value are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("ja-JP-u-ca-foobar-x-lvariant-JP"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("th-TH-u-nu-foobar-x-lvariant-TH"))); + + // Legacy locales with additional extensions are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("no-NO-a-foo-x-lvariant-NY"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("ja-JP-a-foo-x-lvariant-JP"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("th-TH-a-foo-x-lvariant-TH"))); + + // Legacy locales with non-empty script are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("ja-Jpan-JP-u-ca-japanese-x-lvariant-JP"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("th-Thai-TH-u-nu-thai-x-lvariant-TH"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("no-Latn-NO-x-lvariant-NY"))); + // non-canonical, non-legacy locales are invalid assertThrows(IllformedLocaleException.class, () -> new Builder().setLocale(Locale.of("123", "4567", "89")), "123_4567_89"); @@ -1377,6 +1423,38 @@ public class LocaleEnhanceTest { checkDigit(Locale.forLanguageTag("en-u-nu-thai"), '\u0e50'); } + // Test that numeric singletons are supported + @Test + public void numericSingletonRoundTripTest() { + var tag = "en-0-foo"; + var value = "foo"; + var singleton = '0'; + // test `forLanguageTag` + var locale = Locale.forLanguageTag(tag); + assertEquals(value, locale.getExtension(singleton)); + assertEquals(tag, locale.toLanguageTag()); + // test `Locale.Builder` + locale = new Builder() + .setLanguage("en") + .setExtension(singleton, value) + .build(); + assertEquals(value, locale.getExtension(singleton)); + assertEquals(tag, locale.toLanguageTag()); + } + + // Ensure that extlang is only accepted after a 2*3ALPHA language subtag + // That is, the 4 ALPHA and 5*8 ALPHA language subtags should not accept extlangs + @ParameterizedTest + @ValueSource(strings = {"quux", "foobar"}) + public void testExtlangAfterReservedLanguage(String lang) { + String tag = lang + "-baz"; + // Locale.forLanguageTag is lenient and truncates the extlang + assertEquals(lang, Locale.forLanguageTag(tag).toLanguageTag()); + // Locale.Builder is strict and should throw + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLanguageTag(tag)); + } + private void checkCalendar(Locale loc, String expected) { Calendar cal = Calendar.getInstance(loc); assertEquals(expected, cal.getClass().getName(), "Wrong calendar"); diff --git a/test/jdk/java/util/Locale/LocaleMatchingTest.java b/test/jdk/java/util/Locale/LocaleMatchingTest.java index c5d8a00d458..806229ca8eb 100644 --- a/test/jdk/java/util/Locale/LocaleMatchingTest.java +++ b/test/jdk/java/util/Locale/LocaleMatchingTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 7069824 8042360 8032842 8175539 8210443 8242010 8276302 8381644 + * @bug 7069824 8042360 8032842 8175539 8210443 8242010 8276302 8381644 8387261 * @summary Verify implementation for Locale matching. * @run junit/othervm LocaleMatchingTest */ @@ -93,6 +93,7 @@ public class LocaleMatchingTest { {"1996-de-Latn", MAX_WEIGHT}, // Testcase for 8042360 {"en-Latn-1234567890", MAX_WEIGHT}, + {"en", Double.NaN}, }; } @@ -146,6 +147,7 @@ public class LocaleMatchingTest { // Ranges {""}, {"ja;q=3"}, + {"en;q=NaN"} }; } diff --git a/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java b/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java index b8b49406c05..a70ebf9e527 100644 --- a/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java +++ b/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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,7 +22,7 @@ */ /* * @test - * @bug 5102289 6278334 8261179 + * @bug 5102289 6278334 8261179 8387455 * @summary Test the default Control implementation. The expiration * functionality of newBundle, getTimeToLive, and needsReload is * tested by ExpirationTest.sh. The factory methods are tested @@ -171,6 +171,15 @@ public class DefaultControlTest { candidateData.put(Locale.ROOT, new Locale[] { Locale.ROOT }); + // Norwegian Nynorsk + candidateData.put(Locale.of("no", "NO", "NY"), new Locale[] { + Locale.of("nn", "NO"), + Locale.of("nn"), + Locale.of("no", "NO", "NY"), + Locale.of("no", "NO"), + Locale.of("no"), + Locale.ROOT}); + // Norwegian Bokmal candidateData.put(Locale.forLanguageTag("nb-NO-POSIX"), new Locale[] { Locale.forLanguageTag("nb-NO-POSIX"), @@ -188,7 +197,21 @@ public class DefaultControlTest { Locale.forLanguageTag("no"), Locale.forLanguageTag("nb"), Locale.ROOT}); - + // Appears as no-NO-NY legacy locale (but contains script) so treat as Norwegian Bokmal + candidateData.put(Locale.forLanguageTag("no-Latn-NO-x-lvariant-NY"), new Locale[] { + Locale.forLanguageTag("no-Latn-NO-x-lvariant-NY"), + Locale.forLanguageTag("nb-Latn-NO-x-lvariant-NY"), + Locale.forLanguageTag("no-Latn-NO"), + Locale.forLanguageTag("nb-Latn-NO"), + Locale.forLanguageTag("no-Latn"), + Locale.forLanguageTag("nb-Latn"), + Locale.forLanguageTag("no-NO-x-lvariant-NY"), + Locale.forLanguageTag("nb-NO-x-lvariant-NY"), + Locale.forLanguageTag("no-NO"), + Locale.forLanguageTag("nb-NO"), + Locale.forLanguageTag("no"), + Locale.forLanguageTag("nb"), + Locale.ROOT}); for (Locale locale : candidateData.keySet()) { List candidates = CONTROL.getCandidateLocales("any", locale); diff --git a/test/jdk/java/util/TimeZone/AIXTzMappingTest.java b/test/jdk/java/util/TimeZone/AIXTzMappingTest.java new file mode 100644 index 00000000000..6324bb8581a --- /dev/null +++ b/test/jdk/java/util/TimeZone/AIXTzMappingTest.java @@ -0,0 +1,102 @@ +/* + * 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 8380993 + * @library /test/lib + * @summary Validates AIX timezone mapping behavior where POSIX TZ strings + * with comma-separated DST rules are truncated and mapped through tzmappings + * to the expected IANA timezone IDs. + * @requires os.family == "aix" + * @run main/othervm AIXTzMappingTest + */ + +import java.util.TimeZone; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class AIXTzMappingTest { + + // POSIX TZ strings that should be mapped via tzmappings + private static final String TZ_CET = "CET-1CEST,M3.5.0,M10.5.0"; + private static final String TZ_MEZ = "MEZ-1MESZ,M3.5.0,M10.5.0/3"; + + private static final String ID_PARIS = "Europe/Paris"; + private static final String ID_BERLIN = "Europe/Berlin"; + + public static void main(String[] args) throws Throwable { + if (args.length == 0) { + runWithTZ(TZ_CET, ID_PARIS); + runWithTZ(TZ_MEZ, ID_BERLIN); + } else if (args.length == 1) { + runTZTest(args[0]); + } else { + throw new RuntimeException( + "Expected 0 or 1 arguments, got " + args.length); + } + } + + private static void runWithTZ(String tz, String expectedId) + throws Throwable { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "AIXTzMappingTest", expectedId); + + pb.environment().put("TZ", tz); + + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldHaveExitValue(0); + } + + /* + * On AIX, POSIX TZ strings such as: + * CET-1CEST,M3.5.0,M10.5.0 + * MEZ-1MESZ,M3.5.0,M10.5.0/3 + * are truncated at the comma and mapped through tzmappings to + * IANA timezone IDs. + * + * This test verifies that the expected IANA timezone ID is selected. + */ + private static void runTZTest(String expectedId) { + String tzStr = System.getenv("TZ"); + + if (tzStr == null) { + throw new RuntimeException( + "Got unexpected timezone information: TZ is null"); + } + + TimeZone tz = TimeZone.getDefault(); + String tzId = tz.getID(); + + if (!expectedId.equals(tzId)) { + throw new RuntimeException( + "Expected timezone ID " + expectedId + + " but got " + tzId + + " for TZ=" + tzStr); + } + + System.out.println( + "AIX timezone mapping test passed: " + + tzId + " for TZ=" + tzStr); + } +} diff --git a/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java b/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java index 5fd48efadcc..9a332202ab3 100644 --- a/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java +++ b/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -26,7 +26,10 @@ * @library /test/lib * @summary This test will ensure that daylight savings rules are followed * appropriately when setting a custom timezone ID via the TZ env variable. - * @requires os.family != "windows" + * AIX is excluded because it uses a different timezone mapping mechanism + * through the tzmappings file; see AIXTzMappingTest.java for AIX-specific + * coverage. + * @requires os.family != "windows" & os.family != "aix" * @run main/othervm CustomTzIDCheckDST */ diff --git a/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java b/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java index 0f52bc67f08..4a96530e026 100644 --- a/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java +++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java @@ -34,7 +34,7 @@ /* * @test * @bug 4486658 - * @summary Exercise multithreaded maps, by default ConcurrentHashMap. + * @summary Exercise multithreaded maps. * Multithreaded hash table test. Each thread does a random walk * though elements of "key" array. On each iteration, it checks if * table includes key. If absent, with probability pinsert it @@ -42,7 +42,8 @@ * it. (pinsert and premove are expressed as percentages to simplify * parsing from command line.) * @library /test/lib - * @run main/timeout=1600 MapLoops + * @run main/timeout=1600 MapLoops java.util.concurrent.ConcurrentHashMap + * @run main/timeout=1600 MapLoops java.util.concurrent.ConcurrentSkipListMap */ import static java.util.concurrent.TimeUnit.MILLISECONDS; diff --git a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java index 641fbf2e495..f1f32bee310 100644 --- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java +++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java @@ -37,9 +37,7 @@ /* * @test id=default * @summary Conformance testing variant of JSR-166 tck tests. - * @library /test/lib * @build * - * @build jdk.test.lib.Platform * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 JSR166TestCase */ @@ -48,9 +46,7 @@ * @test id=forkjoinpool-common-parallelism * @summary Test implementation details variant of JSR-166 * tck tests with ForkJoinPool common parallelism. - * @library /test/lib * @build * - * @build jdk.test.lib.Platform * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 * --add-opens java.base/java.util.concurrent=ALL-UNNAMED @@ -72,9 +68,7 @@ * @summary Remaining test implementation details variant of * JSR-166 tck tests apart from ForkJoinPool common * parallelism. - * @library /test/lib * @build * - * @build jdk.test.lib.Platform * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 * --add-opens java.base/java.util.concurrent=ALL-UNNAMED @@ -141,7 +135,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; -import jdk.test.lib.Platform; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestResult; @@ -631,13 +624,6 @@ public class JSR166TestCase extends TestCase { "SynchronousQueue20Test", "ReentrantReadWriteLock20Test" }; - - if (Platform.isS390x()) { - java20TestClassNames = new String[] { - "ForkJoinPool20Test", - }; - } - addNamedTestClasses(suite, java20TestClassNames); } diff --git a/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java b/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java index 690c056e23b..706816f4880 100644 --- a/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java +++ b/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java @@ -40,6 +40,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; +import java.util.TreeSet; import junit.framework.Test; @@ -168,6 +169,101 @@ public class PriorityQueueTest extends JSR166TestCase { mustEqual(items[i], q.poll()); } + /** + * Queue contains all elements of collection used to initialize and + * uses the custom comparator provided to order its elements + */ + public void testConstructor8() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q = new PriorityQueue<>(Arrays.asList(items), cmp); + assertEquals(cmp, q.comparator()); + for (int i = SIZE - 1; i >= 0; --i) + mustEqual(items[i], q.poll()); + } + + /** + * Initializing from Collection with a comparator has the order + * of its elements the same as the queue initialized with a comparator + * and populated with Collection after initialization + */ + public void testConstructor9() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>(Arrays.asList(items), cmp); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(SIZE, cmp); + q2.addAll(Arrays.asList(items)); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor10() { + try { + new PriorityQueue((Collection)null, new MyReverseComparator()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor11() { + try { + new PriorityQueue(Arrays.asList(new Item[SIZE]), new MyReverseComparator()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from PriorityQueue and its comparator + */ + public void testConstructor12() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>(cmp); + q1.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(q1, q1.comparator()); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + + /** + * Initializing from SortedSet and its comparator + */ + public void testConstructor13() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + TreeSet s = new TreeSet<>(cmp); + s.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q = new PriorityQueue<>(s, s.comparator()); + for (int i = 0; i < SIZE; ++i) + mustEqual(q.poll(), s.removeFirst()); + } + + /** + * Initializing with null comparator + */ + public void testConstructor14() { + Item[] items = seqItems(SIZE); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>((Comparator) null); + q1.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(Arrays.asList(items), null); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + /** * isEmpty is true before add, false after */ diff --git a/test/jdk/java/util/concurrent/tck/SemaphoreTest.java b/test/jdk/java/util/concurrent/tck/SemaphoreTest.java index 5d45dc21a8e..38ba54645b9 100644 --- a/test/jdk/java/util/concurrent/tck/SemaphoreTest.java +++ b/test/jdk/java/util/concurrent/tck/SemaphoreTest.java @@ -33,11 +33,15 @@ * Pat Fisher, Mike Judd. */ +import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.Collection; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; import junit.framework.Test; import junit.framework.TestSuite; @@ -672,4 +676,39 @@ public class SemaphoreTest extends JSR166TestCase { assertTrue(s.toString().contains("Permits = -2")); } + /** + * Test scenario for JDK-8386085 + * When investigating failures of this test, it is important to know that AbstractQueuedSynchronizer, + * AbstractQueuedLongSynchronizer, and StampedLock all share similar code which was fixed for JDK-8386085 + */ + public void testShortTimeoutAcquisition() throws InterruptedException { + final int width = Runtime.getRuntime().availableProcessors(); + try (var pool = Executors.newFixedThreadPool(width)) { + // Setup + final AtomicBoolean done = new AtomicBoolean(false); + final CountDownLatch waitingToRun = new CountDownLatch(width); + final Semaphore s = new Semaphore(0); + final Callable c = () -> { + waitingToRun.countDown(); + do { + s.tryAcquire(1, MICROSECONDS); // acquisition storm + } while (!done.get()); + return null; + }; + + // Task creation + for(int i = 0; i < width; ++i) + pool.submit(c); + + waitingToRun.await(); // Wait for all tasks to start + Thread.sleep(3000); // Wait a while for acquisitions + s.release(width); // Hand out permits + Thread.sleep(1000); // Wait a while for permit acquisitions + + final int permitsAvailable = s.availablePermits(); + done.set(true); // Ensure that tasks can exit + assertTrue(permitsAvailable < width); // Some permits should've been taken + } + } + } diff --git a/test/jdk/java/util/concurrent/tck/StampedLockTest.java b/test/jdk/java/util/concurrent/tck/StampedLockTest.java index 8fe8aaca569..45500fa581e 100644 --- a/test/jdk/java/util/concurrent/tck/StampedLockTest.java +++ b/test/jdk/java/util/concurrent/tck/StampedLockTest.java @@ -33,8 +33,8 @@ */ import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; - import static java.util.concurrent.locks.StampedLock.isLockStamp; import static java.util.concurrent.locks.StampedLock.isOptimisticReadStamp; import static java.util.concurrent.locks.StampedLock.isReadLockStamp; @@ -45,6 +45,7 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -1527,4 +1528,38 @@ public class StampedLockTest extends JSR166TestCase { checkTimedGet(future, null); } + /** + * test scenario for JDK-8386085 + */ + public void testShortTimeoutAcquisition() throws InterruptedException { + final int width = Runtime.getRuntime().availableProcessors(); + final StampedLock s = new StampedLock(); + try (var pool = Executors.newFixedThreadPool(width)) { + // Setup + final AtomicBoolean done = new AtomicBoolean(false); + final CountDownLatch waitingToRun = new CountDownLatch(width); + final long stamp = s.writeLock(); + assertTrue(s.validate(stamp)); + final Callable c = () -> { + waitingToRun.countDown(); + do { + long lock = s.tryWriteLock(1, MICROSECONDS); // acquisition storm + if (s.validate(lock)) + s.unlockWrite(lock); + } while (!done.get()); + return null; + }; + + // Task creation + for(int i = 0; i < width; ++i) + pool.submit(c); + + waitingToRun.await(); // Wait for all tasks to start + Thread.sleep(3000); // Wait a while for acquisitions + s.unlockWrite(stamp); // Hand out permits + Thread.sleep(1000); // Wait a while for permit acquisitions + done.set(true); // Ensure that tasks can exit + } + assertTrue(s.validate(s.writeLockInterruptibly())); // Should succeed + } } diff --git a/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java b/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java new file mode 100644 index 00000000000..e39b47dfc8e --- /dev/null +++ b/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java @@ -0,0 +1,124 @@ +/* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Random; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import jdk.test.lib.RandomFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/* + * @test + * @summary Verify the behaviour of GZIPInputStream when dealing with InputStream.available() + * on the underlying stream and the jdk.util.gzip.tryReadAheadAfterTrailer + * system property being enabled/disabled + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=true GZIPInputStreamCallsAvailable + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=false GZIPInputStreamCallsAvailable + * @run junit GZIPInputStreamCallsAvailable + */ +class GZIPInputStreamCallsAvailable { + + private static final boolean AVAILABLE_METHOD_INVOCATION_SKIPPED = + Boolean.getBoolean("jdk.util.gzip.tryReadAheadAfterTrailer"); + private static final Random random = RandomFactory.getRandom(); + + private record TestData(byte[] uncompressed, byte[] compressed) { + } + + static List numGZIPMembers() { + return List.of(1, + 33, + random.nextInt(2, 1001) // a reasonably large number of members + ); + } + + /* + * Verify that GZIPInputStream reads and returns the correct decompressed data when: + * - the underlying InputStream.available() returns an accurate value + * - and when the GZIPInputStream isn't expected to call the underlying InputStream.available() + * method + */ + @ParameterizedTest + @MethodSource("numGZIPMembers") + void testMultipleMembers(final int numMembers) throws IOException { + final TestData testData = createGZIPStream(numMembers); + final InputStream underlyingStream = AVAILABLE_METHOD_INVOCATION_SKIPPED + // stream whose available() method isn't expected to be invoked + ? new AlwaysThrowFromAvailable(new ByteArrayInputStream(testData.compressed)) + // stream whose available() will be invoked and returns an accurate value + : new ByteArrayInputStream(testData.compressed); + try (GZIPInputStream gzip = new GZIPInputStream(underlyingStream)) { + final byte[] decompressed = gzip.readAllBytes(); + assertArrayEquals(testData.uncompressed, decompressed, "unexpected decompressed data"); + } + } + + /* + * Creates and returns bytes representing a GZIP stream consisting of the given number of + * members. + */ + private static TestData createGZIPStream(final int numMembers) throws IOException { + final String content = "foo bar hello world from " + GZIPInputStreamCallsAvailable.class; + final ByteArrayOutputStream uncompressed = new ByteArrayOutputStream(); + final ByteArrayOutputStream gzipped = new ByteArrayOutputStream(); + for (int i = 1; i <= numMembers; i++) { + final ByteArrayOutputStream member = new ByteArrayOutputStream(); + try (final OutputStream gzip = new GZIPOutputStream(member)) { + final byte[] memberRawBytes = ("member-" + i + " " + content).getBytes(US_ASCII); + gzip.write(memberRawBytes); + // keep track of the uncompressed content too so that it can be compared for + // equality with the decompressed content + uncompressed.write(memberRawBytes); + } + // write out the GZIP member to the stream which accumulates all the members + gzipped.write(member.toByteArray()); + } + return new TestData(uncompressed.toByteArray(), gzipped.toByteArray()); + } + + private static class AlwaysThrowFromAvailable extends FilterInputStream { + public AlwaysThrowFromAvailable(InputStream in) { + super(in); + } + + @Override + public int available() { + throw new AssertionError(this.getClass().getName() + + ".available() wasn't expected to be invoked"); + } + } +} diff --git a/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java new file mode 100644 index 00000000000..b6dea98c28d --- /dev/null +++ b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java @@ -0,0 +1,409 @@ +/* + * 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.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import jdk.test.lib.RandomFactory; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @summary Verifies that the GZIPInputStream works as expected when the underlying + * InputStream is a blocking stream + * @key randomness + * @library /test/lib + * @build jdk.test.lib.net.URIBuilder jdk.test.lib.RandomFactory + * @run junit GZIPOverBlockingStreams + * @comment verify it behaves the same when jdk.util.gzip.tryReadAheadAfterTrailer system property + * is set to false + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=false GZIPOverBlockingStreams + */ +class GZIPOverBlockingStreams { + + private static final Random random = RandomFactory.getRandom(); + private static final String MEMBER_CONTENT_FORMAT = "Hello member %d, foo bar hello world\n"; + private static final ExecutorService httpServerExecutor = Executors.newCachedThreadPool(); + + private static Server nonHttpServer; + private static HttpServer httpServer; + + + @BeforeAll + static void beforeAll() throws Exception { + // create a socket based (non-HTTP) server + nonHttpServer = new Server(); + nonHttpServer.start(); + System.err.println("(non-HTTP) server started at " + nonHttpServer.getAddress()); + + // create a HTTP server + final InetAddress loopback = InetAddress.getLoopbackAddress(); + final InetSocketAddress serverAddr = new InetSocketAddress(loopback, 0); + httpServer = HttpServer.create(serverAddr, 0); + httpServer.setExecutor(httpServerExecutor); + httpServer.createContext("/", new HttpReqHandler()); + httpServer.start(); + System.err.println("started HTTP server at " + httpServer.getAddress()); + + } + + @AfterAll + static void afterAll() throws Exception { + if (nonHttpServer != null) { + System.err.println("stopping server " + nonHttpServer.getAddress()); + nonHttpServer.close(); + } + if (httpServer != null) { + System.err.println("stopping HTTP server " + httpServer.getAddress()); + httpServer.stop(0); + } + httpServerExecutor.shutdownNow(); + } + + static List numGZIPMembers() { + return List.of(1, + 13, + 42, + random.nextInt(2, 101) // a reasonable number of members, not too many + ); + } + + static List socketStreamTestArgs() { + final List args = new ArrayList<>(); + final List numMembers = numGZIPMembers(); + for (boolean shouldCloseSocket : new boolean[]{true, false}) { + for (int n : numMembers) { + args.add(Arguments.of(n, shouldCloseSocket)); + } + } + return args; + } + + /* + * Verifies that when the GZIPInputStream is used to read GZIP content + * over a socket stream, it does not block when reading past a member trailer to determine + * the presence of a subsequent member. + */ + @ParameterizedTest + @MethodSource("socketStreamTestArgs") + void testSocketStream(final int numMembers, final boolean shouldCloseSocket) throws Exception { + final InetSocketAddress serverAddr = nonHttpServer.getAddress(); + try (final Socket socket = new Socket(serverAddr.getAddress(), serverAddr.getPort())) { + System.err.println("connect established " + socket); + try (final OutputStream os = socket.getOutputStream(); + final DataOutputStream dos = new DataOutputStream(os)) { + // instruct the server side the number of GZIP members we want in the response + dos.writeInt(numMembers); + // instruct the server side whether to close the socket after writing out the + // response + dos.writeBoolean(shouldCloseSocket); + System.err.println("sent request for GZIP stream with " + numMembers + " members"); + // read the response + try (final InputStream in = socket.getInputStream(); + final GZIPInputStream gzipInputStream = new GZIPInputStream(in)) { + final byte[] decompressed = gzipInputStream.readAllBytes(); + System.err.println("read " + decompressed.length + + " bytes of decompressed response"); + // verify it's the expected content + assertDecompressedContent(numMembers, decompressed); + } + } + } + final Throwable serverFailure = nonHttpServer.failure; + if (serverFailure != null) { + fail("Server ran into an error", serverFailure); + } + } + + static List httpTestArgs() { + final List args = new ArrayList<>(); + final List numMembers = numGZIPMembers(); + for (boolean chunkedOrNot : new boolean[]{true, false}) { + for (int n : numMembers) { + args.add(Arguments.of(n, chunkedOrNot)); + } + } + return args; + } + + /* + * Verifies that when the GZIPInputStream is used to read GZIP content, + * over a stream obtained from a HTTP response, it does not block when reading past a member + * trailer to determine the presence of a subsequent member. + */ + @ParameterizedTest + @MethodSource("httpTestArgs") + void testHttpStream(final int numMembers, final boolean httpResponseChunked) throws Exception { + final URI reqURI = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(httpServer.getAddress().getPort()) + .path("/") + .build(); + final HttpURLConnection conn = (HttpURLConnection) reqURI.toURL().openConnection(); + conn.setRequestProperty("numMembers", String.valueOf(numMembers)); + conn.setRequestProperty("chunkedResponse", String.valueOf(httpResponseChunked)); + System.err.println("issuing request " + reqURI); + try (final InputStream in = conn.getInputStream(); + final GZIPInputStream gzipInputStream = new GZIPInputStream(in)) { + final byte[] decompressed = gzipInputStream.readAllBytes(); + System.err.println("read " + decompressed.length + + " bytes of decompressed response"); + assertDecompressedContent(numMembers, decompressed); + } + } + + /* + * Creates and returns bytes representing a GZIP stream consisting of the given number of + * members. + */ + private static byte[] createGZIPStream(final int numMembers) throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (int i = 1; i <= numMembers; i++) { + final ByteArrayOutputStream member = new ByteArrayOutputStream(); + try (final OutputStream gzip = new GZIPOutputStream(member)) { + final String memberContent = String.format(MEMBER_CONTENT_FORMAT, i); + gzip.write(memberContent.getBytes(US_ASCII)); + } + // write out the GZIP member to the stream which accumulates all the members + baos.write(member.toByteArray()); + } + return baos.toByteArray(); + } + + /* + * Verifies that the given decompressed bytes, representing the given number of + * GZIP members, do match the expected content. + */ + private static void assertDecompressedContent(final int numMembers, + final byte[] decompressed) { + final String actual = new String(decompressed, US_ASCII); + final StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= numMembers; i++) { + sb.append(String.format(MEMBER_CONTENT_FORMAT, i)); + } + final String expected = sb.toString(); + assertEquals(expected, actual, "unexpected decompressed content"); + } + + /* + * A server which communicates over a socket to receive a request consisting of an integer + * representing the number of GZIP members to respond with. The server then responds back + * on the socket's OutputStream with GZIP content representing those many members. + */ + private static final class Server implements AutoCloseable, Runnable { + private final ServerSocket serverSocket; + private volatile boolean stop; + private volatile Throwable failure; + + private Server() throws IOException { + this.serverSocket = new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); + } + + private InetSocketAddress getAddress() { + return (InetSocketAddress) this.serverSocket.getLocalSocketAddress(); + } + + @Override + public void close() throws IOException { + this.stop = true; + System.err.println("closing server: " + this.serverSocket); + this.serverSocket.close(); + } + + private void start() { + final Thread t = new Thread(this); + t.setName("server"); + t.setDaemon(true); + t.start(); + } + + private synchronized void recordServerFailure(final Throwable t) { + Throwable previous = this.failure; + if (previous != null && previous != t) { + previous.addSuppressed(t); + return; + } + this.failure = t; + } + + @Override + public void run() { + System.err.println("server started accepting requests at " + this.serverSocket); + try { + doRun(); + } catch (Throwable t) { + if (!stop) { // ignore failures if the server is stopped + recordServerFailure(t); + System.err.println("server ran into error: " + t); + t.printStackTrace(); + } + } finally { + try { + this.close(); + } catch (IOException ioe) { + System.err.println("ignoring excpetion " + + "that happened during closing server: " + ioe); + ioe.printStackTrace(); + } + } + } + + private void doRun() throws Exception { + while (!this.stop) { + // we intentionally do not close the Socket. It's upto the + // sendGZIPResponse(...) method to do that only if the test + // request has instructed it to do so. This allows the test + // method to exercise the case where the socket is open + // but doesn't have any more data to send (and thus read() blocks) + final Socket socket = this.serverSocket.accept(); + System.err.println("accepted connection from " + socket); + // handle the request on a separate thread + final Thread handler = new Thread(() -> { + try { + handleRequest(socket); + } catch (Throwable t) { + // keep track of the failure + recordServerFailure(t); + System.err.println("failure when handling request on socket " + + socket + ", exception: " + t); + t.printStackTrace(); + } + }); + handler.setName("request-handler-" + socket.getRemoteSocketAddress()); + handler.setDaemon(true); + handler.start(); + } + } + + private static void handleRequest(final Socket socket) throws IOException { + final int numMembers; + final boolean shouldCloseSocket; + try { + final InputStream in = socket.getInputStream(); + final DataInputStream dis = new DataInputStream(in); + // read the socket's inputstream to determine how many GZIP members are + // expected in the response stream, by the client + numMembers = dis.readInt(); + // whether the socket should be closed after writing out the response + shouldCloseSocket = dis.readBoolean(); + } catch (IOException ioe) { + // could be a socket connection from an unexpected client, so ignore any + // failure when reading the request + System.err.println("Ignoring exception that happened when reading" + + " request from client socket " + socket + ", exception: " + ioe); + ioe.printStackTrace(); + // close the unexpected client connection + socket.close(); + return; + } + // valid request, respond to it with a GZIP response + sendGZIPResponse(socket, numMembers, shouldCloseSocket); + } + + /* + * Sends GZIP content over the socket's OutputStream. This method closes the socket + * only if the test request (read over the socket's InputStream) instructs it to do so. + */ + private static void sendGZIPResponse(final Socket socket, final int numMembers, + final boolean shouldCloseSocket) throws IOException { + // respond back with a GZIP output, containing the expected number of members + final byte[] gzipResponse = createGZIPStream(numMembers); + System.err.println("responding to " + socket + " with a GZIP stream of size " + + gzipResponse.length + " with " + numMembers + " members"); + final OutputStream os = socket.getOutputStream(); + os.write(gzipResponse); + System.err.println("done responding to " + socket); + // close the socket only if the test request wants us to + if (shouldCloseSocket) { + System.err.println("closing " + socket); + socket.close(); + } + } + } + + /* + * A HTTP request handler which responds back with chunked or non-chunked + * response containing GZIP content. + */ + private static final class HttpReqHandler implements HttpHandler { + + @Override + public void handle(final HttpExchange exchange) throws IOException { + final URI reqURI = exchange.getRequestURI(); + System.err.println("handling request: " + reqURI + " from " + + exchange.getRemoteAddress()); + + final String val = exchange.getRequestHeaders().getFirst("numMembers"); + final int numMembers = Integer.parseInt(val); + final boolean respChunked = Boolean.parseBoolean( + exchange.getRequestHeaders().getFirst("chunkedResponse")); + + final byte[] gzipResponse = createGZIPStream(numMembers); + System.err.println("responding to " + reqURI + + " with a GZIP stream of size " + gzipResponse.length + + " with " + numMembers + " members" + + " with chunked response = " + respChunked); + + // drain the inputstream and write out the response + exchange.getRequestBody().readAllBytes(); + if (respChunked) { + exchange.sendResponseHeaders(200, 0); // 0 = Chunked response + } else { + exchange.sendResponseHeaders(200, gzipResponse.length); + } + try (final OutputStream os = exchange.getResponseBody()) { + os.write(gzipResponse); + } + System.err.println("done responding to " + reqURI); + } + } +} diff --git a/test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java b/test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java new file mode 100644 index 00000000000..42a0de1c405 --- /dev/null +++ b/test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java @@ -0,0 +1,103 @@ +/* + * 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 javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +/* + * @test + * @key headful + * @bug 8381236 + * @summary manual test for VoiceOver that moves Components across Windows + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual VoiceOverHierarchyChangeTest + */ + +public class VoiceOverHierarchyChangeTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + INSTRUCTIONS: + 1. Open VoiceOver + 2. Move the mouse over the "Does Nothing" button + 3. Click the "Move To Other Window" button + 4. Move the mouse over the "Does Nothing" button + + Expected behavior: VoiceOver reads "Does Nothing" after steps + 2 and 4. + """; + + PassFailJFrame.builder() + .title("VoiceOverHierarchyChangeTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(VoiceOverHierarchyChangeTest::createUI) + .build() + .awaitAndCheck(); + } + public static JFrame createUI() { + JFrame f1 = new JFrame(); + f1.getContentPane().setPreferredSize(new Dimension(300, 100)); + JFrame f2 = new JFrame(); + f2.getContentPane().setPreferredSize(new Dimension(300, 100)); + + JButton hopButton = new JButton("Move To Other Window"); + JButton noopButton = new JButton("Does Nothing"); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(hopButton, BorderLayout.NORTH); + panel.add(noopButton, BorderLayout.SOUTH); + + hopButton.addActionListener(e -> { + if (SwingUtilities.isDescendingFrom(hopButton, f1)) { + f2.getContentPane().add(panel); + } else { + f1.getContentPane().add(panel); + } + f1.repaint(); + f2.repaint(); + }); + + f1.getContentPane().add(panel); + f1.pack(); + f2.pack(); + + f1.addComponentListener(new ComponentAdapter() { + @Override + public void componentMoved(ComponentEvent e) { + Rectangle r = f1.getBounds(); + f2.setLocation(r.x, r.y + r.height); + f2.setVisible(true); + } + }); + + return f1; + } +} diff --git a/test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java b/test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..f4336646cbd --- /dev/null +++ b/test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java @@ -0,0 +1,221 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for Cipher + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms CIPHEr.Rsa/ECB/PKCS1Padding true + * @run main/othervm TestLegacyAlgorithms cipheR.rsA true + * @run main/othervm TestLegacyAlgorithms CIPher.what false + * @run main/othervm TestLegacyAlgorithms cipHER.RSA/ECB/PKCS1Padding2 false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=CIPHER.RSA + * -Djdk.crypto.disabledAlgorithms=CIPHER.RSA + * TestLegacyAlgorithms CIPHER.RSA false true + + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.List; +import javax.crypto.Cipher; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final List ALG_LIST = + List.of("Rsa/ECB/PKCS1Padding", "rSA"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated Cipher algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for Cipher " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for Cipher " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated Cipher algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for Cipher: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for Cipher: " + warnS); + } + + private static void checkWarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + private static void warnDisabledTest() + throws Exception { + checkWarn("no warning when the algorithm is disabled", + "RSA", false, () -> { + Utils.runAndCheckException( + () -> Cipher.getInstance("RSA"), + NoSuchAlgorithmException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkWarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultCipher.run(a)); + } + + Provider provider = null; + for (Provider p : Security.getProviders()) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + if (p.getService("Cipher", "RSA") != null) { + provider = p; + break; + } + } + if (provider != null) { + final Provider fp = provider; + for (String a : ALG_LIST) { + checkWarn("provider object " + fp.getName() + + ": alg " + a, a, shouldWarn, + () -> ProvObjCipher.run(a, fp)); + + checkWarn("provider name " + fp.getName() + + ": alg " + a, a, shouldWarn, + () -> ProvNameCipher.run(a, fp)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultCipher { + static void run(String alg) throws Exception { + Cipher c = Cipher.getInstance(alg); + System.out.println(" type lookup: got Cipher w/ alg " + + c.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + c = Cipher.getInstance(alg); + System.out.println(" type lookup again: got Cipher w/ alg " + + c.getAlgorithm()); + } + } + + private static final class ProvObjCipher { + static void run(String alg, Provider provider) throws Exception { + Cipher c = Cipher.getInstance(alg, provider); + System.out.println(" provider object: got Cipher w/ alg " + + c.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + c = Cipher.getInstance(alg, provider); + System.out.println(" provider object again: got Cipher " + + "w/ alg " + c.getAlgorithm()); + } + } + + private static final class ProvNameCipher { + static void run(String alg, Provider provider) throws Exception { + Cipher c = Cipher.getInstance(alg, provider.getName()); + System.out.println(" provider name: got Cipher w/ alg " + + c.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + c = Cipher.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got Cipher " + + "w/ alg " + c.getAlgorithm()); + } + } +} diff --git a/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java b/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java index c97bb819ba2..f0f4c149895 100644 --- a/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java +++ b/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java @@ -23,11 +23,9 @@ /* * @test - * @bug 8364121 - * @summary DESKeySpec.isWeak should throw aiobe exception if the offset is - * negative. + * @bug 8364121 8386473 + * @summary Test DES[ede]KeySpec for negative and integer overflow offsets */ -import java.security.InvalidKeyException; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.DESKeySpec; @@ -52,21 +50,46 @@ public class OffsetKey { boolean weak = DESKeySpec.isWeak(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} - try{ + try { boolean parityAdjusted = DESKeySpec.isParityAdjusted(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + DESKeySpec desKey = new DESKeySpec(strongKey, Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + boolean weak = DESKeySpec.isWeak(strongKey, Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + boolean parityAdjusted = DESKeySpec.isParityAdjusted(strongKey, + Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + // Test triple-DES - try{ + try { DESedeKeySpec desEdeKey = new DESedeKeySpec(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} - try{ + try { boolean parityAdjusted = DESedeKeySpec.isParityAdjusted(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} + + try { + DESedeKeySpec desEdeKey = new DESedeKeySpec(strongKey, + Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + boolean parityAdjusted = DESedeKeySpec.isParityAdjusted(strongKey, + Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} } } diff --git a/test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java b/test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java new file mode 100644 index 00000000000..e1e341fabe0 --- /dev/null +++ b/test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java @@ -0,0 +1,113 @@ +/* + * 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 8384512 + * @summary Test verifies that BMP images are encoded correctly with RLE4 + * compression and odd number of distinct pixels at the end of + * scanline. + */ + +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; + +public class RLE4PaddingTest { + private static final int width = 5; + private static final int height = 2; + private static BufferedImage getTestImage() { + // create BufferedImage with width 5 and all distinct pixels, + // so that it uses absolute mode for RLE. + // If we don't add appropriate padding at end of the scanline, + // the encoded data of next scanline will be corrupt. + int bpp = 4; + int size = 16; + byte[] r = new byte[16]; + byte[] g = new byte[16]; + byte[] b = new byte[16]; + + for (int i = 0; i < 16; i++) { + r[i] = g[i] = b[i] = (byte)(i * 16); + } + IndexColorModel icm = new IndexColorModel(bpp, size, r, g, b); + BufferedImage src = new BufferedImage(width, height, + BufferedImage.TYPE_BYTE_INDEXED, icm); + + int[][] rows = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10} + }; + + for (int y = 0; y < src.getHeight(); y++) { + for (int x = 0; x < src.getWidth(); x++) { + src.getRaster().setSample(x, y, 0, rows[y][x]); + } + } + return src; + } + + public static void main(String[] args) throws IOException { + BufferedImage src = getTestImage(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageOutputStream ios = ImageIO.createImageOutputStream(baos); + ImageWriter writer = ImageIO.getImageWritersByFormatName("BMP").next(); + writer.setOutput(ios); + ImageWriteParam param = writer.getDefaultWriteParam(); + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionType("BI_RLE4"); + writer.write(null, new IIOImage(src, null, null), param); + ios.close(); + baos.close(); + + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + BufferedImage dst = ImageIO.read(bais); + + checkResult(src, dst); + } + + private static void checkResult(BufferedImage src, BufferedImage dst) { + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int srcRgb = src.getRGB(x, y); + int dstRgb = dst.getRGB(x, y); + + if (srcRgb != dstRgb) { + throw new RuntimeException("Test failed due to color" + + " difference: " + Integer.toHexString(dstRgb) + + " instead of " + Integer.toHexString(srcRgb) + + " at [" + x + ", " + y + "]"); + } + } + } + } +} diff --git a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java new file mode 100644 index 00000000000..11f3efb1518 --- /dev/null +++ b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026, IBM Corporation. All rights reserved. + * 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 8387124 + * @summary Test TLS cipher suite disabling via jdk.tls.disabledAlgorithms, + * including matching on bulk cipher components, covering both + * visibility and handshake behavior. + * @library /test/lib + * /javax/net/ssl/TLSCommon + * /javax/net/ssl/templates + * @run main/othervm BulkCipherDisabledAlgorithms visibility + * @run main/othervm BulkCipherDisabledAlgorithms handshake + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.*; + +import jdk.test.lib.process.Proc; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +public class BulkCipherDisabledAlgorithms { + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new RuntimeException("Missing mode argument"); + } + + String mode = args[0]; + boolean isVisibilityTest = "visibility".equals(mode); + boolean isHandshakeTest = "handshake".equals(mode); + + if (args.length == 1) { + List tests = buildTests(isVisibilityTest); + + for (String[] test : tests) { + String suite = test[0]; + String disabled = test[1]; + String expected = test[2]; + + System.out.println("================================================="); + System.out.println("Testing: " + mode + + ", suite=" + suite + + ", disabled=" + disabled + + ", expected=" + expected); + + Proc p = Proc.create( + BulkCipherDisabledAlgorithms.class.getName()) + .args(mode, suite, expected) + .secprop("jdk.tls.disabledAlgorithms", disabled) + .inheritIO(); + + p.start().waitFor(0); + } + + System.out.println("TEST PASS - OK"); + return; + } + + String suite = args[1]; + String expected = args[2]; + boolean expectedDisabled = "disabled".equals(expected); + + if (isVisibilityTest) { + testCipherSuiteVisibility(suite, expectedDisabled); + } + + if (isHandshakeTest) { + testHandshake(suite, expectedDisabled); + } + } + + // Returns cipher suites for testing. + // - true: use all supported suites (independent of disabledAlgorithms) + // - false: use default enabled suites (candidates for handshake) + private static CipherSuite[] getCipherSuites(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + String[] suites = useSupportedSuites + ? engine.getSupportedCipherSuites() + : engine.getEnabledCipherSuites(); + + return Arrays.stream(suites) + .map(CipherSuite::cipherSuite) + .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .toArray(CipherSuite[]::new); + } + + private static List buildTests(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + if (useSupportedSuites) { + // disabledAlgorithms limits supported suites; clear to list all + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + List tests = new ArrayList<>(); + CipherSuite[] suites = getCipherSuites(useSupportedSuites); + + for (CipherSuite suite : suites) { + String suiteName = suite.name(); + String bulk = extractBulkCipher(suiteName); + + tests.add(new String[] { suiteName, suiteName, "disabled" }); + tests.add(new String[] { suiteName, bulk, "disabled" }); + + for (CipherSuite other : suites) { + // Negative test case: disable a different bulk cipher than the one + // used by the current suite. This ensures that the suite remains + // enabled and a successful TLS handshake can still be negotiated. + if (other == suite) { + continue; + } + + String otherBulk = extractBulkCipher(other.name()); + + if (!bulk.equals(otherBulk) + && !suiteName.contains(otherBulk)) { + tests.add(new String[] { suiteName, otherBulk, "enabled" }); + break; + } + } + } + + return tests; + } + + /** + * Separator used in TLS cipher suite names to mark the start of + * the bulk cipher component (e.g. TLS_RSA_WITH_AES_128_CBC_SHA). + */ + private static final String WITH = "_WITH_"; + + private static String extractBulkCipher(String suite) { + if (suite.contains(WITH)) { + String after = suite.substring(suite.indexOf(WITH) + WITH.length()); + int last = after.lastIndexOf('_'); + return after.substring(0, last); + } else { + int first = suite.indexOf('_'); + int last = suite.lastIndexOf('_'); + return suite.substring(first + 1, last); + } + } + + private static void testCipherSuiteVisibility(String suite, boolean expectedDisabled) + throws NoSuchAlgorithmException { + boolean visible = Arrays.asList(getCipherSuites(true)) + .contains(CipherSuite.cipherSuite(suite)); + + if (!expectedDisabled && !visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' not visible but expected to be enabled"); + } else if (expectedDisabled && visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' visible but expected to be disabled"); + } + } + + private static void testHandshake(String suite, boolean expectedDisabled) throws Exception { + try { + new TLSHandshakeTest(suite).run(); + + if (expectedDisabled) { + throw new RuntimeException( + "Handshake succeeded but should fail: " + suite); + } + } catch (SSLHandshakeException e) { + if (!expectedDisabled) { + throw new RuntimeException( + "Handshake failed unexpectedly: " + suite, e); + } + } + } + + private static class TLSHandshakeTest extends SSLSocketTemplate { + private final String suite; + + TLSHandshakeTest(String suite) { + this.suite = suite; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + } +} diff --git a/test/jdk/javax/print/attribute/MediaSizeTest.java b/test/jdk/javax/print/attribute/MediaSizeTest.java new file mode 100644 index 00000000000..91d8ed19178 --- /dev/null +++ b/test/jdk/javax/print/attribute/MediaSizeTest.java @@ -0,0 +1,41 @@ +/* + * 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 8041911 + * @summary Test that MediaSize with non-standard portrait dimensions is OK +*/ + +import javax.print.attribute.standard.MediaSize; +import static javax.print.attribute.standard.MediaSize.INCH; + +public class MediaSizeTest { + + public static void main(String args[]) { + new MediaSize(0, 0, INCH); + new MediaSize(1, 1, INCH); + new MediaSize(2, 1, INCH); + new MediaSize(1, 2, INCH); + } +} diff --git a/test/jdk/javax/script/CommonSetup.sh b/test/jdk/javax/script/CommonSetup.sh index 4b59f9cbbc7..363d679df54 100644 --- a/test/jdk/javax/script/CommonSetup.sh +++ b/test/jdk/javax/script/CommonSetup.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2020, 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,7 @@ case "$OS" in OS="Windows" FS="\\" ;; - CYGWIN* | MSYS* | MINGW* ) + CYGWIN* ) PS=";" OS="Windows" FS="\\" diff --git a/test/jdk/javax/swing/JFileChooser/HTMLFileName.java b/test/jdk/javax/swing/JFileChooser/HTMLFileName.java index a8bc9525cca..d22d8e207dd 100644 --- a/test/jdk/javax/swing/JFileChooser/HTMLFileName.java +++ b/test/jdk/javax/swing/JFileChooser/HTMLFileName.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 @@ -33,7 +33,7 @@ import javax.swing.filechooser.FileSystemView; /* * @test id=metal * @bug 8139228 - * @summary JFileChooser should not render Directory names in HTML format + * @summary JFileChooser should not render directory names in HTML format * @library /java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual HTMLFileName metal @@ -42,15 +42,32 @@ import javax.swing.filechooser.FileSystemView; /* * @test id=system * @bug 8139228 8358532 - * @summary JFileChooser should not render Directory names in HTML format + * @summary JFileChooser should not render directory names in HTML format * @library /java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual HTMLFileName system */ +/* + * @test id=nimbus + * @bug 8139228 + * @summary JFileChooser should not render directory names in HTML format + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HTMLFileName nimbus + */ + +/* + * @test id=motif + * @bug 8139228 + * @summary JFileChooser should not render directory names in HTML format + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HTMLFileName motif + */ + public class HTMLFileName { private static final String INSTRUCTIONS = """ -
    1. JFileChooser shows a virtual directory. The first file in the list has the following name: @@ -86,31 +103,52 @@ public class HTMLFileName { """; + private static final String MOTIF_INSTRUCTIONS = + "

      Note: there's no navigation combo box in Motif. " + + "Ignore it in the instructions.

      \n"; + + + private static volatile String lafName; + + private static String getLafClassName(String lafKey) { + final String lafClassName; + switch (lafKey) { + case "metal" -> lafClassName = UIManager.getCrossPlatformLookAndFeelClassName(); + case "system" -> lafClassName = UIManager.getSystemLookAndFeelClassName(); + case "nimbus" -> lafClassName = "javax.swing.plaf.nimbus.NimbusLookAndFeel"; + case "motif" -> lafClassName = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; + default -> throw new IllegalArgumentException("Unsupported Look-and-Feel keyword: " + lafKey); + } + return lafClassName; + } + public static void main(String[] args) throws Exception { if (args.length < 1) { throw new IllegalArgumentException("Look-and-Feel keyword is required"); } - final String lafClassName; - switch (args[0]) { - case "metal" -> lafClassName = UIManager.getCrossPlatformLookAndFeelClassName(); - case "system" -> lafClassName = UIManager.getSystemLookAndFeelClassName(); - default -> throw new IllegalArgumentException("Unsupported Look-and-Feel keyword: " + args[0]); - } + final String lafClassName = getLafClassName(args[0]); SwingUtilities.invokeAndWait(() -> { try { UIManager.setLookAndFeel(lafClassName); + lafName = UIManager.getLookAndFeel().getName(); } catch (Exception e) { throw new RuntimeException(e); } }); + final boolean motif = "CDE/Motif".equals(lafName); + System.out.println("Test for LookAndFeel " + lafClassName); PassFailJFrame.builder() - .instructions(INSTRUCTIONS) - .columns(45) - .rows(20) + .instructions("\n" + + "

      Look and Feel: " + + lafName + "

      \n" + + (motif ? MOTIF_INSTRUCTIONS : "") + + INSTRUCTIONS) + .columns(motif ? 70 : 45) + .rows(25) .testUI(HTMLFileName::initialize) .positionTestUIBottomRowCentered() .build() @@ -127,7 +165,8 @@ public class HTMLFileName { jfc.putClientProperty("html.disable", htmlDisabled); jfc.setControlButtonsAreShown(false); - JFrame frame = new JFrame(htmlDisabled ? "HTML disabled" : "HTML enabled"); + JFrame frame = new JFrame((htmlDisabled ? "HTML disabled" : "HTML enabled") + + " - " + lafName); frame.add(jfc); frame.pack(); return frame; diff --git a/test/jdk/javax/swing/SwingWorker/TestDoneBeforeDoInBackground.java b/test/jdk/javax/swing/SwingWorker/TestDoneBeforeDoInBackground.java index 56534928f6e..119d9efaab9 100644 --- a/test/jdk/javax/swing/SwingWorker/TestDoneBeforeDoInBackground.java +++ b/test/jdk/javax/swing/SwingWorker/TestDoneBeforeDoInBackground.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -23,6 +23,7 @@ /* * @test * @bug 8081474 + * @library /test/lib * @summary Verifies if SwingWorker calls 'done' * before the 'doInBackground' is finished * @run main TestDoneBeforeDoInBackground @@ -34,22 +35,25 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import jdk.test.lib.Utils; + public class TestDoneBeforeDoInBackground { - private static final int WAIT_TIME = 200; + private static final long WAIT_TIME = Utils.adjustTimeout(200); private static final long CLEANUP_TIME = 1000; private static final AtomicBoolean doInBackgroundStarted = new AtomicBoolean(false); private static final AtomicBoolean doInBackgroundFinished = new AtomicBoolean(false); private static final AtomicBoolean doneFinished = new AtomicBoolean(false); private static final CountDownLatch doneLatch = new CountDownLatch(1); + private static final CountDownLatch workerStarted = new CountDownLatch(1); public static void main(String[] args) throws InterruptedException { SwingWorker worker = new SwingWorker<>() { @Override protected String doInBackground() throws Exception { try { - while (!Thread.currentThread().isInterrupted()) { + while (true) { System.out.println("Working..."); Thread.sleep(WAIT_TIME); } @@ -85,6 +89,12 @@ public class TestDoneBeforeDoInBackground { worker.addPropertyChangeListener( new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { + if (worker.getState() == SwingWorker.StateValue.STARTED) { + // Now the worker has started and we got a STARTED + // notification. It should be save to cancel now. + workerStarted.countDown(); + } + System.out.println("doInBackgroundStarted: " + doInBackgroundStarted.get() + " doInBackgroundFinished: " + @@ -121,7 +131,9 @@ public class TestDoneBeforeDoInBackground { } }); worker.execute(); - Thread.sleep(WAIT_TIME * 3); + if (!workerStarted.await(5 * WAIT_TIME, TimeUnit.MILLISECONDS)) { + throw new RuntimeException("worker didn't start in time"); + } final long start = System.currentTimeMillis(); worker.cancel(true); diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index b2f94596622..7cf6234e88f 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -24,7 +24,7 @@ /* * @test * @summary Testing Classfile stack maps generator. - * @bug 8305990 8320222 8320618 8335475 8338623 8338661 8343436 + * @bug 8305990 8320222 8320618 8335475 8338623 8338661 8343436 8386700 * @build testdata.* * @run junit StackMapsTest */ @@ -415,4 +415,29 @@ class StackMapsTest { assertEquals(2, code.maxLocals()); assertEquals(2, code.maxStack()); } + + @Test + void testStaleLocals() { + byte[] bytes = ClassFile.of().build(ClassDesc.of("Repro"), clb -> clb + .withMethodBody("m", MethodTypeDesc.of(CD_void, CD_int), ACC_STATIC, cob -> { + var cond = cob.newLabel(); + var back = cob.newLabel(); + var fwd = cob.newLabel(); + cob.iconst_0() + .istore(1) // stale slot 1 holding of a long incorrectly clears slot 0 in the second round + .iload(1) + .ifeq(cond) // conditional branch triggers merge of frames + .goto_(fwd) + .labelBinding(cond) + .iload(0) // invalid stack frame when slot 0 is cleared + .pop() + .labelBinding(back) + .return_() + .labelBinding(fwd) + .lconst_0() + .lstore(0) // long overrides slots 0 and 1 + .goto_(back); // back jump with modified locals triggers second round with stale slots + })); + assertEmpty(ClassFile.of().verify(bytes)); + } } diff --git a/test/jdk/jdk/incubator/vector/AbstractVectorConversionTest.java b/test/jdk/jdk/incubator/vector/AbstractVectorConversionTest.java index 6f0625d7985..854ffd407c8 100644 --- a/test/jdk/jdk/incubator/vector/AbstractVectorConversionTest.java +++ b/test/jdk/jdk/incubator/vector/AbstractVectorConversionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, 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 @@ -41,6 +41,7 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.IntFunction; +import jdk.incubator.vector.Float16; abstract class AbstractVectorConversionTest { @@ -156,6 +157,31 @@ abstract class AbstractVectorConversionTest { return a; } + interface ToFloat16F { + short apply(int i); + } + + static short[] fill_float16(int s, ToFloat16F f) { + return fill_float16(new short[s], f); + } + + static short[] fill_float16(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + if (a.length > 7) { + a[0] = Float16.float16ToRawShortBits(Float16.MAX_VALUE); + a[1] = Float16.float16ToRawShortBits(Float16.MIN_VALUE); + a[2] = Float16.float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + a[3] = Float16.float16ToRawShortBits(Float16.POSITIVE_INFINITY); + a[4] = Float16.float16ToRawShortBits(Float16.NaN); + a[5] = (short)0.0; + a[6] = Short.MIN_VALUE; + } + return a; + } + + static final List> BYTE_GENERATORS = List.of( withToString("byte(i)", (int s) -> fill_byte(s, i -> (byte) (i + 1))) ); @@ -180,6 +206,10 @@ abstract class AbstractVectorConversionTest { withToString("double(i)", (int s) -> fill_double(s, i -> (double) (i * 10 + 0.1))) ); + static final List> FLOAT16_GENERATORS = List.of( + withToString("Float16(i)", (int s) -> fill_float16(s, i -> (short) (i * 100 + 1))) + ); + static List sourceGenerators(Class src) { if (src == byte.class) { return BYTE_GENERATORS; @@ -199,6 +229,9 @@ abstract class AbstractVectorConversionTest { else if (src == double.class) { return DOUBLE_GENERATORS; } + else if (src == Float16.class) { + return FLOAT16_GENERATORS; + } else throw new IllegalStateException(); } @@ -206,11 +239,11 @@ abstract class AbstractVectorConversionTest { static Object[][] fixedShapeXFixedShapeSpeciesArgs(VectorShape shape) { List args = new ArrayList<>(); - for (Class srcE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class)) { + for (Class srcE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class, Float16.class)) { VectorSpecies src = VectorSpecies.of(srcE, shape); List srcGens = sourceGenerators(srcE); - for (Class dstE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class)) { + for (Class dstE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class, Float16.class)) { VectorSpecies dst = VectorSpecies.of(dstE, shape); for (Object srcGen : srcGens) { @@ -225,12 +258,12 @@ abstract class AbstractVectorConversionTest { static Object[][] fixedShapeXShapeSpeciesArgs(VectorShape srcShape) { List args = new ArrayList<>(); - for (Class srcE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class)) { + for (Class srcE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class, Float16.class)) { VectorSpecies src = VectorSpecies.of(srcE, srcShape); List srcGens = sourceGenerators(srcE); for (VectorShape dstShape : VectorShape.values()) { - for (Class dstE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class)) { + for (Class dstE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class, Float16.class)) { VectorSpecies dst = VectorSpecies.of(dstE, dstShape); for (Object srcGen : srcGens) { @@ -245,10 +278,10 @@ abstract class AbstractVectorConversionTest { static Object[][] fixedShapeXSegmentedCastSpeciesArgs(VectorShape srcShape, boolean legal) { List args = new ArrayList<>(); - for (Class srcE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class)) { + for (Class srcE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class, Float16.class)) { VectorSpecies src = VectorSpecies.of(srcE, srcShape); for (VectorShape dstShape : VectorShape.values()) { - for (Class dstE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class)) { + for (Class dstE : List.of(byte.class, short.class, int.class, long.class, float.class, double.class, Float16.class)) { VectorSpecies dst = VectorSpecies.of(dstE, dstShape); if (legal == (dst.length() == src.length())) { args.add(new Object[]{src, dst}); @@ -261,6 +294,22 @@ abstract class AbstractVectorConversionTest { public enum ConvAPI {CONVERT, CONVERTSHAPE, CASTSHAPE, REINTERPRETSHAPE} + static Short float16_conversion_adapter(Number in) { + if (in.getClass() == Short.class) + return Float16.float16ToRawShortBits(Float16.valueOf(in.shortValue())); + else if (in.getClass() == Integer.class) + return Float16.float16ToRawShortBits(Float16.valueOf(in.intValue())); + else if (in.getClass() == Long.class) + return Float16.float16ToRawShortBits(Float16.valueOf(in.longValue())); + else if (in.getClass() == Float.class) + return Float16.float16ToRawShortBits(Float16.valueOf(in.floatValue())); + else if (in.getClass() == Double.class) + return Float16.float16ToRawShortBits(Float16.valueOf(in.doubleValue())); + else if (in.getClass() == Byte.class) + return Float16.float16ToRawShortBits(Float16.valueOf(in.byteValue())); + else + throw new IllegalStateException(); + } static Function convertValueFunction(Class to) { if (to == byte.class) @@ -273,6 +322,8 @@ abstract class AbstractVectorConversionTest { return Number::longValue; else if (to == float.class) return Number::floatValue; + else if (to == Float16.class) + return (N) -> float16_conversion_adapter(N); else if (to == double.class) return Number::doubleValue; else @@ -282,7 +333,7 @@ abstract class AbstractVectorConversionTest { static BiConsumer putBufferValueFunction(Class from) { if (from == byte.class) return (bb, o) -> bb.put((byte) o); - else if (from == short.class) + else if (from == short.class || from == Float16.class) return (bb, o) -> bb.putShort((short) o); else if (from == int.class) return (bb, o) -> bb.putInt((int) o); @@ -299,7 +350,7 @@ abstract class AbstractVectorConversionTest { static Function getBufferValueFunction(Class to) { if (to == byte.class) return ByteBuffer::get; - else if (to == short.class) + else if (to == short.class || to == Float16.class) return ByteBuffer::getShort; else if (to == int.class) return ByteBuffer::getInt; @@ -335,10 +386,23 @@ abstract class AbstractVectorConversionTest { static void copyConversionArray(Object src, int srcPos, Object dest, int destPos, int length, + VectorSpecies srcSpecies, + VectorSpecies dstSpecies, Function c) { + if (srcSpecies.elementType() == dstSpecies.elementType()) { + System.arraycopy(src, srcPos, dest, destPos, length); + return; + } for (int i = 0; i < length; i++) { Number v = (Number) Array.get(src, srcPos + i); - Array.set(dest, destPos + i, c.apply(v)); + if (srcSpecies.elementType() == Float16.class) { + v = (Number) Float16.shortBitsToFloat16(v.shortValue()); + } + v = (Number) c.apply(v); + if (dstSpecies.elementType() == Float16.class) { + v = (Number) v.shortValue(); + } + Array.set(dest, destPos + i, v); } } @@ -420,8 +484,14 @@ abstract class AbstractVectorConversionTest { int[] parts = getPartsArray(m, is_contracting_conv); - Object expected = Array.newInstance(destSpecies.elementType(), out_len); - Object actual = Array.newInstance(destSpecies.elementType(), out_len); + Object expected = null, actual = null; + if (destSpecies.elementType() == Float16.class) { + expected = Array.newInstance(short.class, out_len); + actual = Array.newInstance(short.class, out_len); + } else { + expected = Array.newInstance(destSpecies.elementType(), out_len); + actual = Array.newInstance(destSpecies.elementType(), out_len); + } Function convertValue = convertValueFunction(destSpecies.elementType()); @@ -432,11 +502,12 @@ abstract class AbstractVectorConversionTest { if (is_contracting_conv) { int start_idx = -part * src_species_len; zeroArray(expected, j, dst_species_len); - copyConversionArray(in, i, expected, start_idx + j, src_species_len, convertValue); + copyConversionArray(in, i, expected, start_idx + j, src_species_len, srcSpecies, destSpecies, convertValue); } else { int start_idx = part * dst_species_len; - copyConversionArray(in, start_idx + i, expected, j, dst_species_len, convertValue); + copyConversionArray(in, start_idx + i, expected, j, dst_species_len, srcSpecies, destSpecies, convertValue); } + } for (int ic = 0; ic < INVOC_COUNT; ic++) { @@ -452,7 +523,6 @@ abstract class AbstractVectorConversionTest { System.arraycopy(rv.toArray(), 0, actual, j, dst_species_len); } } - Assert.assertEquals(actual, expected); } @@ -469,8 +539,14 @@ abstract class AbstractVectorConversionTest { int[] parts = getPartsArray(m, is_contracting_conv); - Object expected = Array.newInstance(dstSpecies.elementType(), out_len); - Object actual = Array.newInstance(dstSpecies.elementType(), out_len); + Object expected = null, actual = null; + if (dstSpecies.elementType() == Float16.class) { + expected = Array.newInstance(short.class, out_len); + actual = Array.newInstance(short.class, out_len); + } else { + expected = Array.newInstance(dstSpecies.elementType(), out_len); + actual = Array.newInstance(dstSpecies.elementType(), out_len); + } BiConsumer putValue = putBufferValueFunction(srcSpecies.elementType()); Function getValue = getBufferValueFunction(dstSpecies.elementType()); diff --git a/test/jdk/jdk/incubator/vector/Float16Vector128LoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float16Vector128LoadStoreTests.java new file mode 100644 index 00000000000..570be3bde63 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector128LoadStoreTests.java @@ -0,0 +1,1038 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector java.base/jdk.internal.vm.annotation + * @run testng/othervm -XX:-TieredCompilation Float16Vector128LoadStoreTests + * + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.Arena; +import java.lang.foreign.ValueLayout; +import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.internal.vm.annotation.DontInline; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.ByteOrder; +import java.util.List; +import java.util.function.*; + +@Test +public class Float16Vector128LoadStoreTests extends AbstractVectorLoadStoreTest { + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_128; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); + + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0, "at index #" + i); + } + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("short[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i * 5)); + }), + withToString("short[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? 1 : (short)(i + 1))); + }) + ); + + // Relative to array.length + static final List> INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl + 1", (int l) -> { + return l - SPECIES.length() + 1; + }), + withToString("l + speciesl - 1", (int l) -> { + return l + SPECIES.length() - 1; + }), + withToString("l + speciesl", (int l) -> { + return l + SPECIES.length(); + }), + withToString("l + speciesl + 1", (int l) -> { + return l + SPECIES.length() + 1; + }) + ); + + // Relative to byte[] array.length or MemorySegment.byteSize() + static final List> BYTE_INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl*ebsize + 1", (int l) -> { + return l - SPECIES.vectorByteSize() + 1; + }), + withToString("l + speciesl*ebsize - 1", (int l) -> { + return l + SPECIES.vectorByteSize() - 1; + }), + withToString("l + speciesl*ebsize", (int l) -> { + return l + SPECIES.vectorByteSize(); + }), + withToString("l + speciesl*ebsize + 1", (int l) -> { + return l + SPECIES.vectorByteSize() + 1; + }) + ); + + @DataProvider + public Object[][] shortProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentProvider() { + return FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, bo}; + }))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, fm, bo}; + })))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + static MemorySegment toSegment(short[] a, IntFunction fb) { + MemorySegment ms = fb.apply(a.length * SPECIES.elementSize() / 8); + for (int i = 0; i < a.length; i++) { + ms.set(ELEMENT_LAYOUT, i * SPECIES.elementSize() / 8 , a[i]); + } + return ms; + } + + static short[] segmentToArray(MemorySegment ms) { + return ms.toArray(ELEMENT_LAYOUT); + } + + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + @DontInline + static VectorShuffle shuffleFromArray(int[] a, int i) { + return SPECIES.shuffleFromArray(a, i); + } + + @DontInline + static void shuffleIntoArray(VectorShuffle s, int[] a, int i) { + s.intoArray(a, i); + } + + @DontInline + static VectorShuffle shuffleFromMemorySegment(MemorySegment mem, int i, ByteOrder bo) { + return VectorShuffle.fromMemorySegment(SPECIES, mem, i, bo); + } + + @DontInline + static void shuffleIntoMemorySegment(VectorShuffle s, MemorySegment mem, int i, ByteOrder bo) { + s.intoMemorySegment(mem, i, bo); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromArray(a, i); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i, VectorMask m) { + return Float16Vector.fromArray(SPECIES, a, i, m); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i) { + v.intoArray(a, i); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i, VectorMask m) { + v.intoArray(a, i, m); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromMemorySegment(a, i, bo); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo, VectorMask m) { + return Float16Vector.fromMemorySegment(SPECIES, a, i, bo, m); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo) { + v.intoMemorySegment(a, i, bo); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo, VectorMask m) { + v.intoMemorySegment(a, i, bo, m); + } + + @Test(dataProvider = "shortProvider") + static void loadStoreArray(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i); + } + } + assertEquals(r, a); + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void loadArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + fromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void storeArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMaskArray(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, vmask); + av.intoArray(r, i); + } + } + assertArraysEquals(r, a, mask); + + + r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, vmask); + } + } + assertArraysEquals(r, a, mask); + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void loadArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i, vmask); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + fromArray(a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void storeArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i, vmask); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMask(IntFunction fa, + IntFunction fm) { + boolean[] mask = fm.apply(SPECIES.length()); + boolean[] r = new boolean[mask.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < mask.length; i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, mask); + } + + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo); + } + } + long m = r.mismatch(a); + Assert.assertEquals(m, -1, "Segments not equal"); + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder()); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentMaskProvider") + static void loadStoreMemorySegmentMask(IntFunction fa, + IntFunction fb, + IntFunction fm, + ByteOrder bo) { + short[] _a = fa.apply(SPECIES.length()); + MemorySegment a = toSegment(_a, fb); + MemorySegment r = fb.apply((int) a.byteSize()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo, vmask); + av.intoMemorySegment(r, i, bo); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + + + r = fb.apply((int) a.byteSize()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo, vmask); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder(), vmask); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder(), vmask); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreReadonlyMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb).asReadOnly(); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(true)) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(false)) + ); + + VectorMask m = SPECIES.shuffleFromOp(i -> i % 2 == 0 ? 1 : -1) + .laneIsValid(); + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, m) + ); + } + + + @Test(dataProvider = "maskProvider") + static void loadStoreMask(IntFunction fm) { + boolean[] a = fm.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask vmask = SPECIES.loadMask(a, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, a); + } + + + @Test(dataProvider = "shuffleIntProvider") + static void loadStoreShuffleArray(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = VectorShuffle.fromArray(SPECIES, a, i); + shuffle.intoArray(r, i); + } + } + + for (int i = 0; i < a.length; i++) { + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, a[i]), r[i]); + } + + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void storeShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffleIntoArray(shuffle, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + VectorShuffle shuffle = shuffleFromArray(a, index); + shuffleIntoArray(shuffle, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void loadShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffle.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + shuffleFromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntMemorySegmentProvider") + static void loadStoreShuffleMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; //An integer for every lane is read out. So 4 bytes per lane + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = VectorShuffle.fromMemorySegment(SPECIES, a, i, bo); + shuffle.intoMemorySegment(r, i, bo); + } + } + + for (int i = 0; i < l / 4; i++) { + int ai = a.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + int ri = r.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, ai), ri); + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleLoadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = shuffleFromMemorySegment(a, i, ByteOrder.nativeOrder()); + shuffle.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + shuffleFromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleStoreMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + + + // Gather/Scatter load/store tests + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], a[i + indexMap[j]]); + } + } + } catch (AssertionError e) { + assertEquals(r[j], a[i + indexMap[j]], "at index #" + j); + } + } + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0); + } + } + } catch (AssertionError e) { + assertEquals(r[i], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0, "at index #" + j); + } + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + if (mask[j % SPECIES.length()]) { + expected[i + indexMap[j]] = a[j]; + } + } + } + + assertEquals(r, expected); + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + expected[i + indexMap[j]] = a[j]; + } + } + + assertEquals(r, expected); + } + + @DataProvider + public Object[][] gatherScatterProvider() { + return INT_INDEX_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] gatherScatterMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fs -> INT_INDEX_GENERATORS.stream().flatMap(fm -> + FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm, fs}; + }))). + toArray(Object[][]::new); + } + + + @Test(dataProvider = "gatherScatterProvider") + static void gather(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void gatherMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i, vmask); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b, mask); + } + + @Test(dataProvider = "gatherScatterProvider") + static void scatter(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i); + } + } + + assertScatterArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void scatterMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i, vmask); + } + } + + assertScatterArraysEquals(r, a, b, mask); + } + + + +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java new file mode 100644 index 00000000000..215f6ef9c47 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java @@ -0,0 +1,5898 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float16Vector128Tests + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import jdk.incubator.vector.VectorShape; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.Vector; + +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import jdk.incubator.vector.Float16Vector; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.Integer; +import java.util.List; +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Test +public class Float16Vector128Tests extends AbstractVectorTest { + + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_128; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + static void assertEquals(short actual, short expected, short delta) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta)); + } + static void assertEquals(short actual, short expected, short delta, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta), msg); + } + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + static void assertEquals(long actual, long expected) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected)); + } + static void assertEquals(long actual, long expected, String msg) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected), msg); + } + static void assertEquals(String actual, String expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(Object actual, Object expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + static void assertEquals(boolean actual, boolean expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(boolean actual, boolean expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + + + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + private static final short MIN_IDENTITY = float16ToRawShortBits(Float16.POSITIVE_INFINITY); + private static final short MUL_IDENTITY = float16ToRawShortBits(Float16.valueOf(1.0f)); + + // for floating point addition reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_ADD = float16ToRawShortBits(Float16.valueOf(10.0f)); + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_MUL = float16ToRawShortBits(Float16.valueOf(50.0f)); + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); + + static void assertArraysStrictlyEquals(short[] r, short[] a) { + for (int i = 0; i < a.length; i++) { + short ir = r[i]; + short ia = a[i]; + if (ir != ia) { + Assert.fail(String.format("at index #%d, expected = %016X, actual = %016X", i, ia, ir)); + } + } + } + + interface FUnOp { + short apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "at index #" + i + ", input = " + a[i]); + } + } + + interface FUnArrayOp { + short[] apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnArrayOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a[i])); + } + } catch (AssertionError e) { + short[] ref = f.apply(a[i]); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i], "at index #" + i + ", input = " + a[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + interface FReductionOp { + short apply(short[] a, int idx); + } + + interface FReductionAllOp { + short apply(short[] a); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (short)0.0); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa, + short relativeErrorFactor) { + int i = 0; + try { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor)))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor)))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor))), "at index #" + i); + } + } + + interface FReductionMaskedOp { + short apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOp { + short apply(short[] a, boolean[] mask); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (short)0.0); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + short relativeError) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError))))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError))))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError)))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError)))), "at index #" + i); + } + } + + interface FReductionOpLong { + long apply(short[] a, int idx); + } + + interface FReductionAllOpLong { + long apply(short[] a); + } + + static void assertReductionLongArraysEquals(long[] r, long rc, short[] a, + FReductionOpLong f, FReductionAllOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FReductionMaskedOpLong { + long apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOpLong { + long apply(short[] a, boolean[] mask); + } + + static void assertReductionLongArraysEqualsMasked(long[] r, long rc, short[] a, boolean[] mask, + FReductionMaskedOpLong f, FReductionAllMaskedOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + } + } + + interface FBoolReductionOp { + boolean apply(boolean[] a, int idx); + } + + static void assertReductionBoolArraysEquals(boolean[] r, boolean[] a, FBoolReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FMaskReductionOp { + int apply(boolean[] a, int idx); + } + + static void assertMaskReductionArraysEquals(int[] r, boolean[] a, FMaskReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+order[i+j]]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]]); + } + } + + static void assertcompressArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + k], a[i + j]); + k++; + } + } + for (; k < vector_len; k++) { + assertEquals(r[i + k], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + k; + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[idx], a[i + j], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + j], a[i + k]); + k++; + } else { + assertEquals(r[i + j], (short)0); + } + } + } + } catch (AssertionError e) { + int idx = i + j; + if (m[idx % SPECIES.length()]) { + assertEquals(r[idx], a[i + k], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod(shortBitsToFloat16(order[idx]).intValue(), 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a) { + int i = 0; + for (; i < a.length; i += SPECIES.length()) { + int idx = i; + for (int j = idx; j < (idx + SPECIES.length()); j++) + a[j]=a[idx]; + } + + try { + for (i = 0; i < a.length; i++) { + assertEquals(r[i], a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i], "at index #" + i + ", input = " + a[i]); + } + } + + interface FBoolUnOp { + boolean apply(boolean a); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, FBoolUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "(" + a[i] + ") at index #" + i); + } + } + + interface FBoolBinOp { + boolean apply(boolean a, boolean b); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, boolean[] b, FBoolBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + interface FBinOp { + short apply(short a, short b); + } + + interface FBinMaskOp { + short apply(short a, short b, boolean m); + + static FBinMaskOp lift(FBinOp f) { + return (a, b, m) -> m ? f.apply(a, b) : a; + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i])); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i]))); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i]), "left associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i])), "right associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinOp f) { + assertArraysEqualsAssociative(rl, rr, a, b, c, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinMaskOp f) { + int i = 0; + boolean mask_bit = false; + try { + for (; i < a.length; i++) { + mask_bit = mask[i % SPECIES.length()]; + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit)); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit)); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit), "left associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit), "right associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b), "(" + a[i] + ", " + b + ") at index #" + i); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())))); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue()))), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastLongArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j], b[j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertShiftArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", input2 = " + b[j] + ", mask = " + mask[i]); + } + } + + interface FBinConstOp { + short apply(short a); + } + + interface FBinConstMaskOp { + short apply(short a, boolean m); + + static FBinConstMaskOp lift(FBinConstOp f) { + return (a, m) -> m ? f.apply(a) : a; + } + } + + static void assertShiftConstEquals(short[] r, short[] a, FBinConstOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstOp f) { + assertShiftConstEquals(r, a, mask, FBinConstMaskOp.lift(f)); + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", mask = " + mask[i]); + } + } + + interface FTernOp { + short apply(short a, short b, short c); + } + + interface FTernMaskOp { + short apply(short a, short b, short c, boolean m); + + static FTernMaskOp lift(FTernOp f) { + return (a, b, c, m) -> m ? f.apply(a, b, c) : a; + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[i]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernOp f) { + assertArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + c[i]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertAltBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + + static boolean isWithin1Ulp(short actual, short expected) { + Float16 act = shortBitsToFloat16(actual); + Float16 exp = shortBitsToFloat16(expected); + if (Float16.isNaN(exp) && !Float16.isNaN(act)) { + return false; + } else if (!Float16.isNaN(exp) && Float16.isNaN(act)) { + return false; + } + + Float16 low = Float16.nextDown(exp); + Float16 high = Float16.nextUp(exp); + + if (Float16.compare(low, exp) > 0) { + return false; + } + + if (Float16.compare(high, exp) < 0) { + return false; + } + + return true; + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, FUnOp mathf, FUnOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i])), "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i])); + } + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i])), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], b[i])); + } + } + + static void assertBroadcastArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, + FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0 || + isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0, + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected = " + mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + Assert.assertTrue(isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])), + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], + b[(i / SPECIES.length()) * SPECIES.length()])); + } + } + + interface FGatherScatterOp { + short[] apply(short[] a, int ix, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, FGatherScatterOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + " at index #" + i); + } + } + + interface FGatherMaskedOp { + short[] apply(short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + interface FScatterMaskedOp { + short[] apply(short[] r, short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FGatherMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FScatterMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(r, a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(r, a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", r: " + + Arrays.toString(Arrays.copyOfRange(r, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + interface FLaneOp { + short[] apply(short[] a, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, int origin, FLaneOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + interface FLaneBop { + short[] apply(short[] a, short[] b, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, FLaneBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLaneMaskedBop { + short[] apply(short[] a, short[] b, int origin, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, boolean[] mask, FLaneMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLanePartBop { + short[] apply(short[] a, short[] b, int origin, int part, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, FLanePartBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + interface FLanePartMaskedBop { + short[] apply(short[] a, short[] b, int origin, int part, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, boolean[] mask, FLanePartMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + + static short convToFloat16(int i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convIntToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504); + case 1: + return convToFloat16(-65504); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> INT_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[intCornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convIntToFloat16CornerCases(i)); + }) + ); + + static void assertArraysEquals(int[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (int)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (int)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + + static short convToFloat16(long i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convLongToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504L); + case 1: + return convToFloat16(-65504L); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> LONG_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convLongToFloat16CornerCases(i)); + }) + ); + + + static void assertArraysEquals(long[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (long)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (long)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static void assertArraysEquals(double[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (double)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (double)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static short bits(short e) { + return e; + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01f + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (i % 17 == 0) ? cornerCaseValue(i) : Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + // Create combinations of pairs + // @@@ Might be sensitive to order e.g. div by 0 + static final List>> FLOAT16_GENERATOR_PAIRS = + Stream.of(FLOAT16_GENERATORS.get(0)). + flatMap(fa -> FLOAT16_GENERATORS.stream().skip(1).map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] boolUnaryOpProvider() { + return BOOL_ARRAY_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_GENERATOR_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> FLOAT16_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float16[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT16_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortBinaryOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortIndexedOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortBinaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpProvider() { + return FLOAT16_GENERATOR_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return FLOAT16_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_TRIPLES.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoIntUnaryOpProvider() { + return INT_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoLongUnaryOpProvider() { + return LONG_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longMaskProvider() { + return LONG_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskBinaryOpProvider() { + return BOOLEAN_MASK_COMPARE_GENERATOR_PAIRS.stream(). + map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskUnaryOpProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleCompareOpProvider() { + return INT_SHUFFLE_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_SHUFFLE_GENERATORS = List.of( + withToStringBi("shuffle[random]", (Integer l, Integer m) -> { + short[] a = new short[l]; + int upper = m; + for (int i = 0; i < 1; i++) { + a[i] = (short)RAND.nextInt(upper); + } + return a; + }) + ); + + @DataProvider + public Object[][] shortUnaryOpSelectFromProvider() { + return FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpSelectFromMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_COMPARE_GENERATORS = List.of( + withToString("float16[i]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)i); + }), + withToString("float16[i - length / 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - (s * BUFFER_REPS / 2))); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i + 1)); + }), + withToString("float16[i - 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - 2)); + }), + withToString("float16[zigZag(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i%3 == 0 ? (short)i : (i%3 == 1 ? (short)(i + 1) : (short)(i - 2))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + static final List>> FLOAT16_TEST_GENERATOR_ARGS = + FLOAT16_COMPARE_GENERATORS.stream(). + map(fa -> List.of(fa)). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortTestOpProvider() { + return FLOAT16_TEST_GENERATOR_ARGS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTestOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_TEST_GENERATOR_ARGS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_COMPARE_GENERATOR_PAIRS = + FLOAT16_COMPARE_GENERATORS.stream(). + flatMap(fa -> FLOAT16_COMPARE_GENERATORS.stream().map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortCompareOpProvider() { + return FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortCompareOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + static short cornerCaseValue(int i) { + return switch(i % 10) { + case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); + case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); + case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); + case 4 -> float16ToRawShortBits(Float16.NaN); + case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); + default -> float16ToShortBits(Float16.valueOf(-0.0f)); + }; + } + + static final IntFunction fr = (vl) -> { + int length = BUFFER_REPS * vl; + return new short[length]; + }; + + static final IntFunction fmr = (vl) -> { + int length = BUFFER_REPS * vl; + return new boolean[length]; + }; + + static final IntFunction lfr = (vl) -> { + int length = BUFFER_REPS * vl; + return new long[length]; + }; + + static boolean eq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() == bt.floatValue(); + } + + static boolean neq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() != bt.floatValue(); + } + + static boolean lt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() < bt.floatValue(); + } + + static boolean le(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() <= bt.floatValue(); + } + + static boolean gt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() > bt.floatValue(); + } + + static boolean ge(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() >= bt.floatValue(); + } + + static short firstNonZero(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 zero = shortBitsToFloat16((short)0); + return Float16.compare(at, zero) != 0 ? a : b; + } + + static short scalar_add(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.add(at, bt)); + } + + static short scalar_sub(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.subtract(at, bt)); + } + + static short scalar_mul(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.multiply(at, bt)); + + } + static short scalar_max(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.max(at, bt)); + } + + static short scalar_min(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.min(at, bt)); + } + + static short scalar_div(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.divide(at, bt)); + } + + static short scalar_fma(short a, short b, short c) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 ct = shortBitsToFloat16(c); + return float16ToRawShortBits(Float16.fma(at, bt, ct)); + } + + static short scalar_abs(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.abs(at)); + } + + static short scalar_neg(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.negate(at)); + } + + static short scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static boolean isNaN(short a) { + return Float16.isNaN(shortBitsToFloat16(a)); + } + static boolean isFinite(short a) { + return Float16.isFinite(shortBitsToFloat16(a)); + } + static boolean isInfinite(short a) { + return Float16.isInfinite(shortBitsToFloat16(a)); + } + + @Test + static void smokeTest1() { + Float16Vector three = Float16Vector.broadcast(SPECIES, float16ToRawShortBits(Float16.valueOf(-3))); + Float16Vector three2 = (Float16Vector) SPECIES.broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three2).allTrue()); + Float16Vector three3 = three2.broadcast(float16ToRawShortBits(Float16.valueOf(1))).broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three3).allTrue()); + int scale = 2; + Class ETYPE = short.class; + if (ETYPE == double.class || ETYPE == long.class) + scale = 1000000; + else if (ETYPE == byte.class && SPECIES.length() >= 64) + scale = 1; + Float16Vector higher = three.addIndex(scale); + VectorMask m = three.compare(VectorOperators.LE, higher); + assert(m.allTrue()); + m = higher.min(float16ToRawShortBits(Float16.valueOf(-1))).test(VectorOperators.IS_NEGATIVE); + assert(m.allTrue()); + m = higher.test(VectorOperators.IS_FINITE); + assert(m.allTrue()); + short max = higher.reduceLanes(VectorOperators.MAX); + assert(max == float16ToRawShortBits(Float16.add(Float16.valueOf(-3), Float16.multiply(Float16.valueOf(scale), Float16.valueOf((SPECIES.length()-1)))))); + } + + private static short[] + bothToArray(Float16Vector a, Float16Vector b) { + short[] r = new short[a.length() + b.length()]; + a.intoArray(r, 0); + b.intoArray(r, a.length()); + return r; + } + + @Test + static void smokeTest2() { + // Do some zipping and shuffling. + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES,0,1,false).toVector(); + assertEquals(io, io2); + Float16Vector a = io.add((short)1); //[1,2] + Float16Vector b = a.neg(); //[-1,-2] + short[] abValues = bothToArray(a,b); //[1,2,-1,-2] + VectorShuffle zip0 = VectorShuffle.makeZip(SPECIES, 0); + VectorShuffle zip1 = VectorShuffle.makeZip(SPECIES, 1); + Float16Vector zab0 = a.rearrange(zip0,b); //[1,-1] + Float16Vector zab1 = a.rearrange(zip1,b); //[2,-2] + short[] zabValues = bothToArray(zab0, zab1); //[1,-1,2,-2] + // manually zip + short[] manual = new short[zabValues.length]; + for (int i = 0; i < manual.length; i += 2) { + manual[i+0] = abValues[i/2]; + manual[i+1] = abValues[a.length() + i/2]; + } + assertEquals(Arrays.toString(zabValues), Arrays.toString(manual)); + VectorShuffle unz0 = VectorShuffle.makeUnzip(SPECIES, 0); + VectorShuffle unz1 = VectorShuffle.makeUnzip(SPECIES, 1); + Float16Vector uab0 = zab0.rearrange(unz0,zab1); + Float16Vector uab1 = zab0.rearrange(unz1,zab1); + short[] abValues1 = bothToArray(uab0, uab1); + assertEquals(Arrays.toString(abValues), Arrays.toString(abValues1)); + } + + static void iotaShuffle() { + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES, 0 , 1, false).toVector(); + assertEquals(io, io2); + } + + @Test + // Test all shuffle related operations. + static void shuffleTest() { + // To test backend instructions, make sure that C2 is used. + for (int loop = 0; loop < INVOC_COUNT * INVOC_COUNT; loop++) { + iotaShuffle(); + } + } + + @Test + void viewAsIntegeralLanesTest() { + Vector asIntegral = SPECIES.zero().viewAsIntegralLanes(); + VectorSpecies asIntegralSpecies = asIntegral.species(); + Assert.assertNotEquals(asIntegralSpecies.elementType(), SPECIES.elementType()); + assertEquals(asIntegralSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asIntegralSpecies.length(), SPECIES.length()); + assertEquals(asIntegral.viewAsFloatingLanes().species(), SPECIES); + } + + @Test + void viewAsFloatingLanesTest() { + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + assertEquals(asFloating.species(), SPECIES); + } + + static short ADD(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::ADD); + } + + static short add(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::add); + } + + static short SUB(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void SUBFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::SUB); + } + + static short sub(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void SUBFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::SUB); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::sub); + } + + static short MUL(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MULFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::MUL); + } + + static short mul(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void MULFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::MUL); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::mul); + } + + static short DIV(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void DIVFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::DIV); + } + + static short div(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void DIVFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::DIV); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::div); + } + + static short FIRST_NONZERO(short a, short b) { + return (short)(firstNonZero(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void FIRST_NONZEROFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void FIRST_NONZEROFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector128Tests::add); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector128Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector128Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector128Tests::div); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector128TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue()).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, Float16Vector128Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector128TestsBroadcastMaskedLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector128Tests::ADD); + } + + static Float16Vector bv_MIN = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINFloat16Vector128TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector128Tests::MIN); + } + + static Float16Vector bv_min = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void minFloat16Vector128TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(bv_min).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector128Tests::min); + } + + static Float16Vector bv_MIN_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINFloat16Vector128TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector128Tests::MIN); + } + + static Float16Vector bv_MAX = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXFloat16Vector128TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector128Tests::MAX); + } + + static Float16Vector bv_max = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void maxFloat16Vector128TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(bv_max).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector128Tests::max); + } + + static Float16Vector bv_MAX_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXFloat16Vector128TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector128Tests::MAX); + } + + static short MIN(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MIN, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::MIN); + } + + static short min(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.min(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::min); + } + + static short MAX(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MAX, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::MAX); + } + + static short max(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.max(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::max); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::MIN); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::min); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::MAX); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector128Tests::max); + } + + static short ADDReduce(short[] a, int idx) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAll(short[] a) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector128Tests::ADDReduce, Float16Vector128Tests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + assertEquals((short) (scalar_add(id, id)), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_add(id, x)), x); + assertEquals((short) (scalar_add(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_add(id, x)), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_add(x, id)), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAllMasked(short[] a, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector128Tests::ADDReduceMasked, Float16Vector128Tests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + static short MULReduce(short[] a, int idx) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAll(short[] a) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector128Tests::MULReduce, Float16Vector128Tests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + assertEquals((short) (scalar_mul(id, id)), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_mul(id, x)), x); + assertEquals((short) (scalar_mul(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_mul(id, x)), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_mul(x, id)), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAllMasked(short[] a, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MULReduceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector128Tests::MULReduceMasked, Float16Vector128Tests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + static short MINReduce(short[] a, int idx) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAll(short[] a) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector128Tests::MINReduce, Float16Vector128Tests::MINReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + assertEquals(scalar_min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_min(id, x), x); + assertEquals(scalar_min(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAllMasked(short[] a, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINReduceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector128Tests::MINReduceMasked, Float16Vector128Tests::MINReduceAllMasked); + } + + static short MAXReduce(short[] a, int idx) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAll(short[] a) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector128Tests::MAXReduce, Float16Vector128Tests::MAXReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + assertEquals(scalar_max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_max(id, x), x); + assertEquals(scalar_max(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAllMasked(short[] a, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXReduceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector128Tests::MAXReduceMasked, Float16Vector128Tests::MAXReduceAllMasked); + } + + static short FIRST_NONZEROReduce(short[] a, int idx) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAll(short[] a) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector128Tests::FIRST_NONZEROReduce, Float16Vector128Tests::FIRST_NONZEROReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(firstNonZero(id, x), x); + assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void FIRST_NONZEROReduceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector128Tests::FIRST_NONZEROReduceMasked, Float16Vector128Tests::FIRST_NONZEROReduceAllMasked); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void withFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0, j = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.withLane(j, b[i + j]).intoArray(r, i); + a[i + j] = b[i + j]; + j = (j + 1) & (SPECIES.length() - 1); + } + } + + + assertArraysStrictlyEquals(r, a); + } + + static boolean testIS_DEFAULT(short a) { + return bits(a)==0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_DEFAULTFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_DEFAULT(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_DEFAULTMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_DEFAULT(a[i + j])); + } + } + } + } + + static boolean testIS_NEGATIVE(short a) { + return bits(a)<0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NEGATIVEFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NEGATIVE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NEGATIVEMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NEGATIVE(a[i + j])); + } + } + } + } + + static boolean testIS_FINITE(short a) { + return isFinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_FINITEFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_FINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_FINITEMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_FINITE(a[i + j])); + } + } + } + } + + static boolean testIS_NAN(short a) { + return isNaN(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NANFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NAN(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NANMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NAN(a[i + j])); + } + } + } + } + + static boolean testIS_INFINITE(short a) { + return isInfinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_INFINITEFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_INFINITEMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.lt(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GTFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GTFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.eq(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void NEFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void NEFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LEFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LEFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GEFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GEFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector128TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector128TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector128TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector128TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + static short blend(short a, short b, boolean mask) { + return mask ? b : a; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector128Tests(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.blend(bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector128Tests::blend); + } + + @Test(dataProvider = "shortUnaryOpShuffleProvider") + static void RearrangeFloat16Vector128Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i)).intoArray(r, i); + } + } + + assertRearrangeArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpShuffleMaskProvider") + static void RearrangeFloat16Vector128TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i), vmask).intoArray(r, i); + } + + assertRearrangeArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void compressFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.compress(vmask).intoArray(r, i); + } + } + + assertcompressArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void expandFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.expand(vmask).intoArray(r, i); + } + } + + assertexpandArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void getFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int num_lanes = SPECIES.length(); + // Manually unroll because full unroll happens after intrinsification. + // Unroll is needed because get intrinsic requires for index to be a known constant. + if (num_lanes == 1) { + r[i]=av.lane(0); + } else if (num_lanes == 2) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + } else if (num_lanes == 4) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + } else if (num_lanes == 8) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + } else if (num_lanes == 16) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + } else if (num_lanes == 32) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + } else if (num_lanes == 64) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + r[i+32]=av.lane(32); + r[i+33]=av.lane(33); + r[i+34]=av.lane(34); + r[i+35]=av.lane(35); + r[i+36]=av.lane(36); + r[i+37]=av.lane(37); + r[i+38]=av.lane(38); + r[i+39]=av.lane(39); + r[i+40]=av.lane(40); + r[i+41]=av.lane(41); + r[i+42]=av.lane(42); + r[i+43]=av.lane(43); + r[i+44]=av.lane(44); + r[i+45]=av.lane(45); + r[i+46]=av.lane(46); + r[i+47]=av.lane(47); + r[i+48]=av.lane(48); + r[i+49]=av.lane(49); + r[i+50]=av.lane(50); + r[i+51]=av.lane(51); + r[i+52]=av.lane(52); + r[i+53]=av.lane(53); + r[i+54]=av.lane(54); + r[i+55]=av.lane(55); + r[i+56]=av.lane(56); + r[i+57]=av.lane(57); + r[i+58]=av.lane(58); + r[i+59]=av.lane(59); + r[i+60]=av.lane(60); + r[i+61]=av.lane(61); + r[i+62]=av.lane(62); + r[i+63]=av.lane(63); + } else { + for (int j = 0; j < SPECIES.length(); j++) { + r[i+j]=av.lane(j); + } + } + } + } + + assertArraysStrictlyEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void BroadcastFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, a[i]).intoArray(r, i); + } + } + + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ZeroFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.zero(SPECIES).intoArray(a, i); + } + } + + assertEquals(a, r); + } + + static short[] sliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else + res[i] = (short)0; + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sliceUnaryFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.slice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector128Tests::sliceUnary); + } + + static short[] sliceBinary(short[] a, short[] b, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void sliceBinaryFloat16Vector128TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, Float16Vector128Tests::sliceBinary); + } + + static short[] slice(short[] a, short[] b, int origin, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = mask[i] ? a[idx+i+origin] : (short)0; + else { + res[i] = mask[i] ? b[idx+j] : (short)0; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void sliceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, mask, Float16Vector128Tests::slice); + } + + static short[] unsliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i < origin) + res[i] = (short)0; + else { + res[i] = a[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void unsliceUnaryFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.unslice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector128Tests::unsliceUnary); + } + + static short[] unsliceBinary(short[] a, short[] b, int origin, int part, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (part == 0) { + if (i < origin) + res[i] = b[idx+i]; + else { + res[i] = a[idx+j]; + j++; + } + } else if (part == 1) { + if (i < origin) + res[i] = a[idx+SPECIES.length()-origin+i]; + else { + res[i] = b[idx+origin+j]; + j++; + } + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void unsliceBinaryFloat16Vector128TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, Float16Vector128Tests::unsliceBinary); + } + + static short[] unslice(short[] a, short[] b, int origin, int part, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = b[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + for (int i = 0; i < SPECIES.length(); i++){ + res[i] = mask[i] ? a[idx+i] : res[i]; + } + short[] res1 = new short[SPECIES.length()]; + if (part == 0) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = b[idx+i]; + else { + res1[i] = res[j]; + j++; + } + } + } else if (part == 1) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = res[SPECIES.length()-origin+i]; + else { + res1[i] = b[idx+origin+j]; + j++; + } + } + } + return res1; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void unsliceFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, mask, Float16Vector128Tests::unslice); + } + + static short SIN(short a) { + return (short)(scalar_sin(a)); + } + + static short strictSIN(short a) { + return (short)(strict_scalar_sin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::SIN, Float16Vector128Tests::strictSIN); + } + + static short EXP(short a) { + return (short)(scalar_exp(a)); + } + + static short strictEXP(short a) { + return (short)(strict_scalar_exp(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXP).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::EXP, Float16Vector128Tests::strictEXP); + } + + static short LOG1P(short a) { + return (short)(scalar_log1p(a)); + } + + static short strictLOG1P(short a) { + return (short)(strict_scalar_log1p(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG1PFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG1P).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::LOG1P, Float16Vector128Tests::strictLOG1P); + } + + static short LOG(short a) { + return (short)(scalar_log(a)); + } + + static short strictLOG(short a) { + return (short)(strict_scalar_log(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOGFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::LOG, Float16Vector128Tests::strictLOG); + } + + static short LOG10(short a) { + return (short)(scalar_log10(a)); + } + + static short strictLOG10(short a) { + return (short)(strict_scalar_log10(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG10Float16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG10).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::LOG10, Float16Vector128Tests::strictLOG10); + } + + static short EXPM1(short a) { + return (short)(scalar_expm1(a)); + } + + static short strictEXPM1(short a) { + return (short)(strict_scalar_expm1(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPM1Float16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXPM1).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::EXPM1, Float16Vector128Tests::strictEXPM1); + } + + static short COS(short a) { + return (short)(scalar_cos(a)); + } + + static short strictCOS(short a) { + return (short)(strict_scalar_cos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::COS, Float16Vector128Tests::strictCOS); + } + + static short TAN(short a) { + return (short)(scalar_tan(a)); + } + + static short strictTAN(short a) { + return (short)(strict_scalar_tan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::TAN, Float16Vector128Tests::strictTAN); + } + + static short SINH(short a) { + return (short)(scalar_sinh(a)); + } + + static short strictSINH(short a) { + return (short)(strict_scalar_sinh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINHFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SINH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::SINH, Float16Vector128Tests::strictSINH); + } + + static short COSH(short a) { + return (short)(scalar_cosh(a)); + } + + static short strictCOSH(short a) { + return (short)(strict_scalar_cosh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSHFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COSH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::COSH, Float16Vector128Tests::strictCOSH); + } + + static short TANH(short a) { + return (short)(scalar_tanh(a)); + } + + static short strictTANH(short a) { + return (short)(strict_scalar_tanh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANHFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TANH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::TANH, Float16Vector128Tests::strictTANH); + } + + static short ASIN(short a) { + return (short)(scalar_asin(a)); + } + + static short strictASIN(short a) { + return (short)(strict_scalar_asin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ASINFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ASIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::ASIN, Float16Vector128Tests::strictASIN); + } + + static short ACOS(short a) { + return (short)(scalar_acos(a)); + } + + static short strictACOS(short a) { + return (short)(strict_scalar_acos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ACOSFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ACOS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::ACOS, Float16Vector128Tests::strictACOS); + } + + static short ATAN(short a) { + return (short)(scalar_atan(a)); + } + + static short strictATAN(short a) { + return (short)(strict_scalar_atan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ATANFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ATAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::ATAN, Float16Vector128Tests::strictATAN); + } + + static short CBRT(short a) { + return (short)(scalar_cbrt(a)); + } + + static short strictCBRT(short a) { + return (short)(strict_scalar_cbrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void CBRTFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.CBRT).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector128Tests::CBRT, Float16Vector128Tests::strictCBRT); + } + + static short HYPOT(short a, short b) { + return (short)(scalar_hypot(a, b)); + } + + static short strictHYPOT(short a, short b) { + return (short)(strict_scalar_hypot(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void HYPOTFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.HYPOT, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector128Tests::HYPOT, Float16Vector128Tests::strictHYPOT); + } + + + static short POW(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictPOW(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.POW, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector128Tests::POW, Float16Vector128Tests::strictPOW); + } + + + static short pow(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictpow(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.pow(bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector128Tests::pow, Float16Vector128Tests::strictpow); + } + + + static short ATAN2(short a, short b) { + return (short)(scalar_atan2(a, b)); + } + + static short strictATAN2(short a, short b) { + return (short)(strict_scalar_atan2(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ATAN2Float16Vector128Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ATAN2, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector128Tests::ATAN2, Float16Vector128Tests::strictATAN2); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.POW, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector128Tests::POW, Float16Vector128Tests::strictPOW); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.pow(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector128Tests::pow, Float16Vector128Tests::strictpow); + } + + + static short FMA(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + static short fma(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector128Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector128Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.fma(bv, cv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, c, Float16Vector128Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector128TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, mask, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i]).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a, b, c, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector128TestsAltBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv).intoArray(r, i); + } + assertAltBroadcastArraysEquals(r, a, b, c, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, c, mask, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector128TestsAltBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv, vmask).intoArray(r, i); + } + + assertAltBroadcastArraysEquals(r, a, b, c, mask, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector128TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector128Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector128TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.fma(b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector128Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector128TestsDoubleBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i], vmask).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, Float16Vector128Tests::FMA); + } + + static short NEG(short a) { + return (short)(scalar_neg((short)a)); + } + + static short neg(short a) { + return (short)(scalar_neg((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void NEGFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::NEG); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void negFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.neg().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::neg); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void NEGMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector128Tests::NEG); + } + + static short ABS(short a) { + return (short)(scalar_abs((short)a)); + } + + static short abs(short a) { + return (short)(scalar_abs((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ABSFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::ABS); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void absFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.abs().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::abs); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ABSMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector128Tests::ABS); + } + + static short SQRT(short a) { + return (short)(scalar_sqrt(a)); + } + + static short sqrt(short a) { + return (short)(scalar_sqrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SQRTFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::SQRT); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sqrtFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sqrt().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::sqrt); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void SQRTMaskedFloat16Vector128Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector128Tests::SQRT); + } + + static boolean band(boolean a, boolean b) { + return a & b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.and(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::band); + } + + static boolean bor(boolean a, boolean b) { + return a | b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskorFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.or(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::bor); + } + + static boolean bxor(boolean a, boolean b) { + return a != b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskxorFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.xor(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::bxor); + } + + static boolean bandNot(boolean a, boolean b) { + return a & !b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandNotFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.andNot(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::bandNot); + } + + static boolean beq(boolean a, boolean b) { + return a == b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskeqFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.eq(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector128Tests::beq); + } + + static boolean unot(boolean a) { + return !a; + } + + @Test(dataProvider = "boolMaskUnaryOpProvider") + static void masknotFloat16Vector128Tests(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + av.not().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector128Tests::unot); + } + + private static final long LONG_MASK_BITS = 0xFFFFFFFFFFFFFFFFL >>> (64 - SPECIES.length()); + + static void assertArraysEquals(long[] r, long[] a, long bits) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], a[i] & bits); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i] & bits, "(" + a[i] + ") at index #" + i); + } + } + + @Test(dataProvider = "longMaskProvider") + static void maskFromToLongFloat16Vector128Tests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = new long[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i++) { + VectorMask vmask = VectorMask.fromLong(SPECIES, a[i]); + r[i] = vmask.toLong(); + } + } + assertArraysEquals(r, a, LONG_MASK_BITS); + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector128TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.lt(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector128TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.eq(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shorttoIntUnaryOpProvider") + static void toIntArrayFloat16Vector128TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int[] r = av.toIntArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void toLongArrayFloat16Vector128TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + long[] r = av.toLongArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toDoubleArrayFloat16Vector128TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + double[] r = av.toDoubleArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toStringFloat16Vector128TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + String str = av.toString(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void hashCodeFloat16Vector128TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int hash = av.hashCode(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + + + static long ADDReduceLong(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return (long)res; + } + + static long ADDReduceAllLong(short[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLong(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceLongFloat16Vector128Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEquals(r, ra, a, + Float16Vector128Tests::ADDReduceLong, Float16Vector128Tests::ADDReduceAllLong); + } + + static long ADDReduceLongMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) { + res = scalar_add(res, a[i]); + } + } + + return (long)res; + } + + static long ADDReduceAllLongMasked(short[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLongMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceLongFloat16Vector128TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD, vmask); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEqualsMasked(r, ra, a, mask, + Float16Vector128Tests::ADDReduceLongMasked, Float16Vector128Tests::ADDReduceAllLongMasked); + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void BroadcastLongFloat16Vector128TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, shortBitsToFloat16(a[i]).longValue()).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector128TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.blend(shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + } + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector128Tests::blend); + } + + + @Test(dataProvider = "shortUnaryOpSelectFromProvider") + static void SelectFromFloat16Vector128Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat16Vector128Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector idxv = Float16Vector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") + static void SelectFromFloat16Vector128TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av, vmask).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleMiscellaneousFloat16Vector128TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + int hash = shuffle.hashCode(); + int length = shuffle.length(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + assertEquals(length, SPECIES.length()); + } + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleToStringFloat16Vector128TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + String str = shuffle.toString(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + Assert.assertTrue(str.equals("Shuffle" + Arrays.toString(subarr)), "at index " + + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + } + } + + @Test(dataProvider = "shuffleCompareOpProvider") + static void shuffleEqualsFloat16Vector128TestsSmokeTest(BiFunction fa, BiFunction fb) { + int[] a = fa.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + int[] b = fb.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = VectorShuffle.fromArray(SPECIES, a, i); + var bv = VectorShuffle.fromArray(SPECIES, b, i); + boolean eq = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(eq, Arrays.equals(a, i, to, b, i, to)); + } + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskEqualsFloat16Vector128Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = SPECIES.loadMask(a, i); + var bv = SPECIES.loadMask(b, i); + boolean equals = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(equals, Arrays.equals(a, i, to, b, i, to)); + } + } + } + + @Test(dataProvider = "maskProvider") + static void maskHashCodeFloat16Vector128TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + int hash = vmask.hashCode(); + + boolean subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static int maskTrueCount(boolean[] a, int idx) { + int trueCount = 0; + for (int i = idx; i < idx + SPECIES.length(); i++) { + trueCount += a[i] ? 1 : 0; + } + return trueCount; + } + + @Test(dataProvider = "maskProvider") + static void maskTrueCountFloat16Vector128TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.trueCount(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector128Tests::maskTrueCount); + } + + static int maskLastTrue(boolean[] a, int idx) { + int i = idx + SPECIES.length() - 1; + for (; i >= idx; i--) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskLastTrueFloat16Vector128TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.lastTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector128Tests::maskLastTrue); + } + + static int maskFirstTrue(boolean[] a, int idx) { + int i = idx; + for (; i < idx + SPECIES.length(); i++) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskFirstTrueFloat16Vector128TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.firstTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector128Tests::maskFirstTrue); + } + + @Test(dataProvider = "maskProvider") + static void maskCompressFloat16Vector128TestsSmokeTest(IntFunction fa) { + int trueCount = 0; + boolean[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + trueCount = vmask.trueCount(); + var rmask = vmask.compress(); + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(rmask.laneIsSet(j), j < trueCount); + } + } + } + } + + @DataProvider + public static Object[][] offsetProvider() { + return new Object[][]{ + {0}, + {-1}, + {+1}, + {+2}, + {-2}, + }; + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeFloat16Vector128TestsSmokeTest(int offset) { + int limit = SPECIES.length() * BUFFER_REPS; + for (int i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + int index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeLongFloat16Vector128TestsSmokeTest(int offset) { + long limit = SPECIES.length() * BUFFER_REPS; + for (long i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + long index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @DataProvider + public static Object[][] lengthProvider() { + return new Object[][]{ + {0}, + {1}, + {32}, + {37}, + {1024}, + {1024+1}, + {1024+5}, + }; + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundFloat16Vector128TestsSmokeTest(int length) { + int actualLoopBound = SPECIES.loopBound(length); + int expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundLongFloat16Vector128TestsSmokeTest(int _length) { + long length = _length; + long actualLoopBound = SPECIES.loopBound(length); + long expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test + static void ElementSizeFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + int elsize = av.elementSize(); + assertEquals(elsize, Float16.SIZE); + } + + @Test + static void VectorShapeFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + assert(vsh.equals(VectorShape.S_128_BIT)); + } + + @Test + static void ShapeWithLanesFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = vsh.withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void ElementTypeFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementType() == Float16.class); + } + + @Test + static void SpeciesElementSizeFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementSize() == Float16.SIZE); + } + + @Test + static void VectorTypeFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().vectorType() == av.getClass()); + } + + @Test + static void WithLanesFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorSpecies species = av.species().withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void WithShapeFloat16Vector128TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = av.species().withShape(vsh); + assert(species.equals(SPECIES)); + } + + @Test + static void MaskAllTrueFloat16Vector128TestsSmokeTest() { + for (int ic = 0; ic < INVOC_COUNT; ic++) { + assertEquals(SPECIES.maskAll(true).toLong(), -1L >>> (64 - SPECIES.length())); + } + } +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector256LoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float16Vector256LoadStoreTests.java new file mode 100644 index 00000000000..dac06d6925f --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector256LoadStoreTests.java @@ -0,0 +1,1038 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector java.base/jdk.internal.vm.annotation + * @run testng/othervm -XX:-TieredCompilation Float16Vector256LoadStoreTests + * + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.Arena; +import java.lang.foreign.ValueLayout; +import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.internal.vm.annotation.DontInline; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.ByteOrder; +import java.util.List; +import java.util.function.*; + +@Test +public class Float16Vector256LoadStoreTests extends AbstractVectorLoadStoreTest { + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_256; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); + + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0, "at index #" + i); + } + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("short[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i * 5)); + }), + withToString("short[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? 1 : (short)(i + 1))); + }) + ); + + // Relative to array.length + static final List> INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl + 1", (int l) -> { + return l - SPECIES.length() + 1; + }), + withToString("l + speciesl - 1", (int l) -> { + return l + SPECIES.length() - 1; + }), + withToString("l + speciesl", (int l) -> { + return l + SPECIES.length(); + }), + withToString("l + speciesl + 1", (int l) -> { + return l + SPECIES.length() + 1; + }) + ); + + // Relative to byte[] array.length or MemorySegment.byteSize() + static final List> BYTE_INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl*ebsize + 1", (int l) -> { + return l - SPECIES.vectorByteSize() + 1; + }), + withToString("l + speciesl*ebsize - 1", (int l) -> { + return l + SPECIES.vectorByteSize() - 1; + }), + withToString("l + speciesl*ebsize", (int l) -> { + return l + SPECIES.vectorByteSize(); + }), + withToString("l + speciesl*ebsize + 1", (int l) -> { + return l + SPECIES.vectorByteSize() + 1; + }) + ); + + @DataProvider + public Object[][] shortProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentProvider() { + return FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, bo}; + }))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, fm, bo}; + })))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + static MemorySegment toSegment(short[] a, IntFunction fb) { + MemorySegment ms = fb.apply(a.length * SPECIES.elementSize() / 8); + for (int i = 0; i < a.length; i++) { + ms.set(ELEMENT_LAYOUT, i * SPECIES.elementSize() / 8 , a[i]); + } + return ms; + } + + static short[] segmentToArray(MemorySegment ms) { + return ms.toArray(ELEMENT_LAYOUT); + } + + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + @DontInline + static VectorShuffle shuffleFromArray(int[] a, int i) { + return SPECIES.shuffleFromArray(a, i); + } + + @DontInline + static void shuffleIntoArray(VectorShuffle s, int[] a, int i) { + s.intoArray(a, i); + } + + @DontInline + static VectorShuffle shuffleFromMemorySegment(MemorySegment mem, int i, ByteOrder bo) { + return VectorShuffle.fromMemorySegment(SPECIES, mem, i, bo); + } + + @DontInline + static void shuffleIntoMemorySegment(VectorShuffle s, MemorySegment mem, int i, ByteOrder bo) { + s.intoMemorySegment(mem, i, bo); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromArray(a, i); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i, VectorMask m) { + return Float16Vector.fromArray(SPECIES, a, i, m); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i) { + v.intoArray(a, i); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i, VectorMask m) { + v.intoArray(a, i, m); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromMemorySegment(a, i, bo); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo, VectorMask m) { + return Float16Vector.fromMemorySegment(SPECIES, a, i, bo, m); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo) { + v.intoMemorySegment(a, i, bo); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo, VectorMask m) { + v.intoMemorySegment(a, i, bo, m); + } + + @Test(dataProvider = "shortProvider") + static void loadStoreArray(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i); + } + } + assertEquals(r, a); + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void loadArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + fromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void storeArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMaskArray(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, vmask); + av.intoArray(r, i); + } + } + assertArraysEquals(r, a, mask); + + + r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, vmask); + } + } + assertArraysEquals(r, a, mask); + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void loadArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i, vmask); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + fromArray(a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void storeArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i, vmask); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMask(IntFunction fa, + IntFunction fm) { + boolean[] mask = fm.apply(SPECIES.length()); + boolean[] r = new boolean[mask.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < mask.length; i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, mask); + } + + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo); + } + } + long m = r.mismatch(a); + Assert.assertEquals(m, -1, "Segments not equal"); + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder()); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentMaskProvider") + static void loadStoreMemorySegmentMask(IntFunction fa, + IntFunction fb, + IntFunction fm, + ByteOrder bo) { + short[] _a = fa.apply(SPECIES.length()); + MemorySegment a = toSegment(_a, fb); + MemorySegment r = fb.apply((int) a.byteSize()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo, vmask); + av.intoMemorySegment(r, i, bo); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + + + r = fb.apply((int) a.byteSize()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo, vmask); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder(), vmask); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder(), vmask); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreReadonlyMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb).asReadOnly(); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(true)) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(false)) + ); + + VectorMask m = SPECIES.shuffleFromOp(i -> i % 2 == 0 ? 1 : -1) + .laneIsValid(); + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, m) + ); + } + + + @Test(dataProvider = "maskProvider") + static void loadStoreMask(IntFunction fm) { + boolean[] a = fm.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask vmask = SPECIES.loadMask(a, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, a); + } + + + @Test(dataProvider = "shuffleIntProvider") + static void loadStoreShuffleArray(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = VectorShuffle.fromArray(SPECIES, a, i); + shuffle.intoArray(r, i); + } + } + + for (int i = 0; i < a.length; i++) { + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, a[i]), r[i]); + } + + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void storeShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffleIntoArray(shuffle, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + VectorShuffle shuffle = shuffleFromArray(a, index); + shuffleIntoArray(shuffle, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void loadShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffle.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + shuffleFromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntMemorySegmentProvider") + static void loadStoreShuffleMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; //An integer for every lane is read out. So 4 bytes per lane + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = VectorShuffle.fromMemorySegment(SPECIES, a, i, bo); + shuffle.intoMemorySegment(r, i, bo); + } + } + + for (int i = 0; i < l / 4; i++) { + int ai = a.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + int ri = r.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, ai), ri); + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleLoadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = shuffleFromMemorySegment(a, i, ByteOrder.nativeOrder()); + shuffle.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + shuffleFromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleStoreMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + + + // Gather/Scatter load/store tests + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], a[i + indexMap[j]]); + } + } + } catch (AssertionError e) { + assertEquals(r[j], a[i + indexMap[j]], "at index #" + j); + } + } + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0); + } + } + } catch (AssertionError e) { + assertEquals(r[i], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0, "at index #" + j); + } + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + if (mask[j % SPECIES.length()]) { + expected[i + indexMap[j]] = a[j]; + } + } + } + + assertEquals(r, expected); + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + expected[i + indexMap[j]] = a[j]; + } + } + + assertEquals(r, expected); + } + + @DataProvider + public Object[][] gatherScatterProvider() { + return INT_INDEX_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] gatherScatterMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fs -> INT_INDEX_GENERATORS.stream().flatMap(fm -> + FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm, fs}; + }))). + toArray(Object[][]::new); + } + + + @Test(dataProvider = "gatherScatterProvider") + static void gather(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void gatherMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i, vmask); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b, mask); + } + + @Test(dataProvider = "gatherScatterProvider") + static void scatter(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i); + } + } + + assertScatterArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void scatterMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i, vmask); + } + } + + assertScatterArraysEquals(r, a, b, mask); + } + + + +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java new file mode 100644 index 00000000000..f198a4a970f --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java @@ -0,0 +1,5898 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float16Vector256Tests + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import jdk.incubator.vector.VectorShape; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.Vector; + +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import jdk.incubator.vector.Float16Vector; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.Integer; +import java.util.List; +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Test +public class Float16Vector256Tests extends AbstractVectorTest { + + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_256; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + static void assertEquals(short actual, short expected, short delta) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta)); + } + static void assertEquals(short actual, short expected, short delta, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta), msg); + } + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + static void assertEquals(long actual, long expected) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected)); + } + static void assertEquals(long actual, long expected, String msg) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected), msg); + } + static void assertEquals(String actual, String expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(Object actual, Object expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + static void assertEquals(boolean actual, boolean expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(boolean actual, boolean expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + + + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + private static final short MIN_IDENTITY = float16ToRawShortBits(Float16.POSITIVE_INFINITY); + private static final short MUL_IDENTITY = float16ToRawShortBits(Float16.valueOf(1.0f)); + + // for floating point addition reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_ADD = float16ToRawShortBits(Float16.valueOf(10.0f)); + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_MUL = float16ToRawShortBits(Float16.valueOf(50.0f)); + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); + + static void assertArraysStrictlyEquals(short[] r, short[] a) { + for (int i = 0; i < a.length; i++) { + short ir = r[i]; + short ia = a[i]; + if (ir != ia) { + Assert.fail(String.format("at index #%d, expected = %016X, actual = %016X", i, ia, ir)); + } + } + } + + interface FUnOp { + short apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "at index #" + i + ", input = " + a[i]); + } + } + + interface FUnArrayOp { + short[] apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnArrayOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a[i])); + } + } catch (AssertionError e) { + short[] ref = f.apply(a[i]); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i], "at index #" + i + ", input = " + a[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + interface FReductionOp { + short apply(short[] a, int idx); + } + + interface FReductionAllOp { + short apply(short[] a); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (short)0.0); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa, + short relativeErrorFactor) { + int i = 0; + try { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor)))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor)))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor))), "at index #" + i); + } + } + + interface FReductionMaskedOp { + short apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOp { + short apply(short[] a, boolean[] mask); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (short)0.0); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + short relativeError) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError))))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError))))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError)))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError)))), "at index #" + i); + } + } + + interface FReductionOpLong { + long apply(short[] a, int idx); + } + + interface FReductionAllOpLong { + long apply(short[] a); + } + + static void assertReductionLongArraysEquals(long[] r, long rc, short[] a, + FReductionOpLong f, FReductionAllOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FReductionMaskedOpLong { + long apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOpLong { + long apply(short[] a, boolean[] mask); + } + + static void assertReductionLongArraysEqualsMasked(long[] r, long rc, short[] a, boolean[] mask, + FReductionMaskedOpLong f, FReductionAllMaskedOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + } + } + + interface FBoolReductionOp { + boolean apply(boolean[] a, int idx); + } + + static void assertReductionBoolArraysEquals(boolean[] r, boolean[] a, FBoolReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FMaskReductionOp { + int apply(boolean[] a, int idx); + } + + static void assertMaskReductionArraysEquals(int[] r, boolean[] a, FMaskReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+order[i+j]]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]]); + } + } + + static void assertcompressArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + k], a[i + j]); + k++; + } + } + for (; k < vector_len; k++) { + assertEquals(r[i + k], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + k; + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[idx], a[i + j], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + j], a[i + k]); + k++; + } else { + assertEquals(r[i + j], (short)0); + } + } + } + } catch (AssertionError e) { + int idx = i + j; + if (m[idx % SPECIES.length()]) { + assertEquals(r[idx], a[i + k], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod(shortBitsToFloat16(order[idx]).intValue(), 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a) { + int i = 0; + for (; i < a.length; i += SPECIES.length()) { + int idx = i; + for (int j = idx; j < (idx + SPECIES.length()); j++) + a[j]=a[idx]; + } + + try { + for (i = 0; i < a.length; i++) { + assertEquals(r[i], a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i], "at index #" + i + ", input = " + a[i]); + } + } + + interface FBoolUnOp { + boolean apply(boolean a); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, FBoolUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "(" + a[i] + ") at index #" + i); + } + } + + interface FBoolBinOp { + boolean apply(boolean a, boolean b); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, boolean[] b, FBoolBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + interface FBinOp { + short apply(short a, short b); + } + + interface FBinMaskOp { + short apply(short a, short b, boolean m); + + static FBinMaskOp lift(FBinOp f) { + return (a, b, m) -> m ? f.apply(a, b) : a; + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i])); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i]))); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i]), "left associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i])), "right associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinOp f) { + assertArraysEqualsAssociative(rl, rr, a, b, c, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinMaskOp f) { + int i = 0; + boolean mask_bit = false; + try { + for (; i < a.length; i++) { + mask_bit = mask[i % SPECIES.length()]; + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit)); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit)); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit), "left associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit), "right associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b), "(" + a[i] + ", " + b + ") at index #" + i); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())))); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue()))), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastLongArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j], b[j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertShiftArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", input2 = " + b[j] + ", mask = " + mask[i]); + } + } + + interface FBinConstOp { + short apply(short a); + } + + interface FBinConstMaskOp { + short apply(short a, boolean m); + + static FBinConstMaskOp lift(FBinConstOp f) { + return (a, m) -> m ? f.apply(a) : a; + } + } + + static void assertShiftConstEquals(short[] r, short[] a, FBinConstOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstOp f) { + assertShiftConstEquals(r, a, mask, FBinConstMaskOp.lift(f)); + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", mask = " + mask[i]); + } + } + + interface FTernOp { + short apply(short a, short b, short c); + } + + interface FTernMaskOp { + short apply(short a, short b, short c, boolean m); + + static FTernMaskOp lift(FTernOp f) { + return (a, b, c, m) -> m ? f.apply(a, b, c) : a; + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[i]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernOp f) { + assertArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + c[i]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertAltBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + + static boolean isWithin1Ulp(short actual, short expected) { + Float16 act = shortBitsToFloat16(actual); + Float16 exp = shortBitsToFloat16(expected); + if (Float16.isNaN(exp) && !Float16.isNaN(act)) { + return false; + } else if (!Float16.isNaN(exp) && Float16.isNaN(act)) { + return false; + } + + Float16 low = Float16.nextDown(exp); + Float16 high = Float16.nextUp(exp); + + if (Float16.compare(low, exp) > 0) { + return false; + } + + if (Float16.compare(high, exp) < 0) { + return false; + } + + return true; + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, FUnOp mathf, FUnOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i])), "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i])); + } + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i])), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], b[i])); + } + } + + static void assertBroadcastArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, + FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0 || + isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0, + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected = " + mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + Assert.assertTrue(isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])), + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], + b[(i / SPECIES.length()) * SPECIES.length()])); + } + } + + interface FGatherScatterOp { + short[] apply(short[] a, int ix, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, FGatherScatterOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + " at index #" + i); + } + } + + interface FGatherMaskedOp { + short[] apply(short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + interface FScatterMaskedOp { + short[] apply(short[] r, short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FGatherMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FScatterMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(r, a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(r, a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", r: " + + Arrays.toString(Arrays.copyOfRange(r, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + interface FLaneOp { + short[] apply(short[] a, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, int origin, FLaneOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + interface FLaneBop { + short[] apply(short[] a, short[] b, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, FLaneBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLaneMaskedBop { + short[] apply(short[] a, short[] b, int origin, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, boolean[] mask, FLaneMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLanePartBop { + short[] apply(short[] a, short[] b, int origin, int part, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, FLanePartBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + interface FLanePartMaskedBop { + short[] apply(short[] a, short[] b, int origin, int part, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, boolean[] mask, FLanePartMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + + static short convToFloat16(int i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convIntToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504); + case 1: + return convToFloat16(-65504); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> INT_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[intCornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convIntToFloat16CornerCases(i)); + }) + ); + + static void assertArraysEquals(int[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (int)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (int)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + + static short convToFloat16(long i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convLongToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504L); + case 1: + return convToFloat16(-65504L); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> LONG_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convLongToFloat16CornerCases(i)); + }) + ); + + + static void assertArraysEquals(long[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (long)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (long)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static void assertArraysEquals(double[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (double)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (double)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static short bits(short e) { + return e; + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01f + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (i % 17 == 0) ? cornerCaseValue(i) : Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + // Create combinations of pairs + // @@@ Might be sensitive to order e.g. div by 0 + static final List>> FLOAT16_GENERATOR_PAIRS = + Stream.of(FLOAT16_GENERATORS.get(0)). + flatMap(fa -> FLOAT16_GENERATORS.stream().skip(1).map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] boolUnaryOpProvider() { + return BOOL_ARRAY_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_GENERATOR_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> FLOAT16_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float16[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT16_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortBinaryOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortIndexedOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortBinaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpProvider() { + return FLOAT16_GENERATOR_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return FLOAT16_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_TRIPLES.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoIntUnaryOpProvider() { + return INT_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoLongUnaryOpProvider() { + return LONG_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longMaskProvider() { + return LONG_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskBinaryOpProvider() { + return BOOLEAN_MASK_COMPARE_GENERATOR_PAIRS.stream(). + map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskUnaryOpProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleCompareOpProvider() { + return INT_SHUFFLE_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_SHUFFLE_GENERATORS = List.of( + withToStringBi("shuffle[random]", (Integer l, Integer m) -> { + short[] a = new short[l]; + int upper = m; + for (int i = 0; i < 1; i++) { + a[i] = (short)RAND.nextInt(upper); + } + return a; + }) + ); + + @DataProvider + public Object[][] shortUnaryOpSelectFromProvider() { + return FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpSelectFromMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_COMPARE_GENERATORS = List.of( + withToString("float16[i]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)i); + }), + withToString("float16[i - length / 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - (s * BUFFER_REPS / 2))); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i + 1)); + }), + withToString("float16[i - 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - 2)); + }), + withToString("float16[zigZag(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i%3 == 0 ? (short)i : (i%3 == 1 ? (short)(i + 1) : (short)(i - 2))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + static final List>> FLOAT16_TEST_GENERATOR_ARGS = + FLOAT16_COMPARE_GENERATORS.stream(). + map(fa -> List.of(fa)). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortTestOpProvider() { + return FLOAT16_TEST_GENERATOR_ARGS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTestOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_TEST_GENERATOR_ARGS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_COMPARE_GENERATOR_PAIRS = + FLOAT16_COMPARE_GENERATORS.stream(). + flatMap(fa -> FLOAT16_COMPARE_GENERATORS.stream().map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortCompareOpProvider() { + return FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortCompareOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + static short cornerCaseValue(int i) { + return switch(i % 10) { + case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); + case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); + case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); + case 4 -> float16ToRawShortBits(Float16.NaN); + case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); + default -> float16ToShortBits(Float16.valueOf(-0.0f)); + }; + } + + static final IntFunction fr = (vl) -> { + int length = BUFFER_REPS * vl; + return new short[length]; + }; + + static final IntFunction fmr = (vl) -> { + int length = BUFFER_REPS * vl; + return new boolean[length]; + }; + + static final IntFunction lfr = (vl) -> { + int length = BUFFER_REPS * vl; + return new long[length]; + }; + + static boolean eq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() == bt.floatValue(); + } + + static boolean neq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() != bt.floatValue(); + } + + static boolean lt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() < bt.floatValue(); + } + + static boolean le(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() <= bt.floatValue(); + } + + static boolean gt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() > bt.floatValue(); + } + + static boolean ge(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() >= bt.floatValue(); + } + + static short firstNonZero(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 zero = shortBitsToFloat16((short)0); + return Float16.compare(at, zero) != 0 ? a : b; + } + + static short scalar_add(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.add(at, bt)); + } + + static short scalar_sub(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.subtract(at, bt)); + } + + static short scalar_mul(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.multiply(at, bt)); + + } + static short scalar_max(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.max(at, bt)); + } + + static short scalar_min(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.min(at, bt)); + } + + static short scalar_div(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.divide(at, bt)); + } + + static short scalar_fma(short a, short b, short c) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 ct = shortBitsToFloat16(c); + return float16ToRawShortBits(Float16.fma(at, bt, ct)); + } + + static short scalar_abs(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.abs(at)); + } + + static short scalar_neg(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.negate(at)); + } + + static short scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static boolean isNaN(short a) { + return Float16.isNaN(shortBitsToFloat16(a)); + } + static boolean isFinite(short a) { + return Float16.isFinite(shortBitsToFloat16(a)); + } + static boolean isInfinite(short a) { + return Float16.isInfinite(shortBitsToFloat16(a)); + } + + @Test + static void smokeTest1() { + Float16Vector three = Float16Vector.broadcast(SPECIES, float16ToRawShortBits(Float16.valueOf(-3))); + Float16Vector three2 = (Float16Vector) SPECIES.broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three2).allTrue()); + Float16Vector three3 = three2.broadcast(float16ToRawShortBits(Float16.valueOf(1))).broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three3).allTrue()); + int scale = 2; + Class ETYPE = short.class; + if (ETYPE == double.class || ETYPE == long.class) + scale = 1000000; + else if (ETYPE == byte.class && SPECIES.length() >= 64) + scale = 1; + Float16Vector higher = three.addIndex(scale); + VectorMask m = three.compare(VectorOperators.LE, higher); + assert(m.allTrue()); + m = higher.min(float16ToRawShortBits(Float16.valueOf(-1))).test(VectorOperators.IS_NEGATIVE); + assert(m.allTrue()); + m = higher.test(VectorOperators.IS_FINITE); + assert(m.allTrue()); + short max = higher.reduceLanes(VectorOperators.MAX); + assert(max == float16ToRawShortBits(Float16.add(Float16.valueOf(-3), Float16.multiply(Float16.valueOf(scale), Float16.valueOf((SPECIES.length()-1)))))); + } + + private static short[] + bothToArray(Float16Vector a, Float16Vector b) { + short[] r = new short[a.length() + b.length()]; + a.intoArray(r, 0); + b.intoArray(r, a.length()); + return r; + } + + @Test + static void smokeTest2() { + // Do some zipping and shuffling. + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES,0,1,false).toVector(); + assertEquals(io, io2); + Float16Vector a = io.add((short)1); //[1,2] + Float16Vector b = a.neg(); //[-1,-2] + short[] abValues = bothToArray(a,b); //[1,2,-1,-2] + VectorShuffle zip0 = VectorShuffle.makeZip(SPECIES, 0); + VectorShuffle zip1 = VectorShuffle.makeZip(SPECIES, 1); + Float16Vector zab0 = a.rearrange(zip0,b); //[1,-1] + Float16Vector zab1 = a.rearrange(zip1,b); //[2,-2] + short[] zabValues = bothToArray(zab0, zab1); //[1,-1,2,-2] + // manually zip + short[] manual = new short[zabValues.length]; + for (int i = 0; i < manual.length; i += 2) { + manual[i+0] = abValues[i/2]; + manual[i+1] = abValues[a.length() + i/2]; + } + assertEquals(Arrays.toString(zabValues), Arrays.toString(manual)); + VectorShuffle unz0 = VectorShuffle.makeUnzip(SPECIES, 0); + VectorShuffle unz1 = VectorShuffle.makeUnzip(SPECIES, 1); + Float16Vector uab0 = zab0.rearrange(unz0,zab1); + Float16Vector uab1 = zab0.rearrange(unz1,zab1); + short[] abValues1 = bothToArray(uab0, uab1); + assertEquals(Arrays.toString(abValues), Arrays.toString(abValues1)); + } + + static void iotaShuffle() { + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES, 0 , 1, false).toVector(); + assertEquals(io, io2); + } + + @Test + // Test all shuffle related operations. + static void shuffleTest() { + // To test backend instructions, make sure that C2 is used. + for (int loop = 0; loop < INVOC_COUNT * INVOC_COUNT; loop++) { + iotaShuffle(); + } + } + + @Test + void viewAsIntegeralLanesTest() { + Vector asIntegral = SPECIES.zero().viewAsIntegralLanes(); + VectorSpecies asIntegralSpecies = asIntegral.species(); + Assert.assertNotEquals(asIntegralSpecies.elementType(), SPECIES.elementType()); + assertEquals(asIntegralSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asIntegralSpecies.length(), SPECIES.length()); + assertEquals(asIntegral.viewAsFloatingLanes().species(), SPECIES); + } + + @Test + void viewAsFloatingLanesTest() { + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + assertEquals(asFloating.species(), SPECIES); + } + + static short ADD(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::ADD); + } + + static short add(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::add); + } + + static short SUB(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void SUBFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::SUB); + } + + static short sub(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void SUBFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::SUB); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::sub); + } + + static short MUL(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MULFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::MUL); + } + + static short mul(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void MULFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::MUL); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::mul); + } + + static short DIV(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void DIVFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::DIV); + } + + static short div(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void DIVFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::DIV); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::div); + } + + static short FIRST_NONZERO(short a, short b) { + return (short)(firstNonZero(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void FIRST_NONZEROFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void FIRST_NONZEROFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector256Tests::add); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector256Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector256Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector256Tests::div); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector256TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue()).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, Float16Vector256Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector256TestsBroadcastMaskedLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector256Tests::ADD); + } + + static Float16Vector bv_MIN = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINFloat16Vector256TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector256Tests::MIN); + } + + static Float16Vector bv_min = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void minFloat16Vector256TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(bv_min).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector256Tests::min); + } + + static Float16Vector bv_MIN_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINFloat16Vector256TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector256Tests::MIN); + } + + static Float16Vector bv_MAX = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXFloat16Vector256TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector256Tests::MAX); + } + + static Float16Vector bv_max = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void maxFloat16Vector256TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(bv_max).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector256Tests::max); + } + + static Float16Vector bv_MAX_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXFloat16Vector256TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector256Tests::MAX); + } + + static short MIN(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MIN, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::MIN); + } + + static short min(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.min(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::min); + } + + static short MAX(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MAX, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::MAX); + } + + static short max(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.max(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::max); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::MIN); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::min); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::MAX); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector256Tests::max); + } + + static short ADDReduce(short[] a, int idx) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAll(short[] a) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector256Tests::ADDReduce, Float16Vector256Tests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + assertEquals((short) (scalar_add(id, id)), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_add(id, x)), x); + assertEquals((short) (scalar_add(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_add(id, x)), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_add(x, id)), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAllMasked(short[] a, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector256Tests::ADDReduceMasked, Float16Vector256Tests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + static short MULReduce(short[] a, int idx) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAll(short[] a) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector256Tests::MULReduce, Float16Vector256Tests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + assertEquals((short) (scalar_mul(id, id)), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_mul(id, x)), x); + assertEquals((short) (scalar_mul(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_mul(id, x)), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_mul(x, id)), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAllMasked(short[] a, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MULReduceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector256Tests::MULReduceMasked, Float16Vector256Tests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + static short MINReduce(short[] a, int idx) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAll(short[] a) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector256Tests::MINReduce, Float16Vector256Tests::MINReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + assertEquals(scalar_min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_min(id, x), x); + assertEquals(scalar_min(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAllMasked(short[] a, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINReduceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector256Tests::MINReduceMasked, Float16Vector256Tests::MINReduceAllMasked); + } + + static short MAXReduce(short[] a, int idx) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAll(short[] a) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector256Tests::MAXReduce, Float16Vector256Tests::MAXReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + assertEquals(scalar_max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_max(id, x), x); + assertEquals(scalar_max(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAllMasked(short[] a, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXReduceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector256Tests::MAXReduceMasked, Float16Vector256Tests::MAXReduceAllMasked); + } + + static short FIRST_NONZEROReduce(short[] a, int idx) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAll(short[] a) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector256Tests::FIRST_NONZEROReduce, Float16Vector256Tests::FIRST_NONZEROReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(firstNonZero(id, x), x); + assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void FIRST_NONZEROReduceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector256Tests::FIRST_NONZEROReduceMasked, Float16Vector256Tests::FIRST_NONZEROReduceAllMasked); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void withFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0, j = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.withLane(j, b[i + j]).intoArray(r, i); + a[i + j] = b[i + j]; + j = (j + 1) & (SPECIES.length() - 1); + } + } + + + assertArraysStrictlyEquals(r, a); + } + + static boolean testIS_DEFAULT(short a) { + return bits(a)==0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_DEFAULTFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_DEFAULT(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_DEFAULTMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_DEFAULT(a[i + j])); + } + } + } + } + + static boolean testIS_NEGATIVE(short a) { + return bits(a)<0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NEGATIVEFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NEGATIVE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NEGATIVEMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NEGATIVE(a[i + j])); + } + } + } + } + + static boolean testIS_FINITE(short a) { + return isFinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_FINITEFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_FINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_FINITEMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_FINITE(a[i + j])); + } + } + } + } + + static boolean testIS_NAN(short a) { + return isNaN(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NANFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NAN(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NANMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NAN(a[i + j])); + } + } + } + } + + static boolean testIS_INFINITE(short a) { + return isInfinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_INFINITEFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_INFINITEMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.lt(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GTFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GTFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.eq(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void NEFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void NEFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LEFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LEFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GEFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GEFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector256TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector256TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector256TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector256TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + static short blend(short a, short b, boolean mask) { + return mask ? b : a; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector256Tests(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.blend(bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector256Tests::blend); + } + + @Test(dataProvider = "shortUnaryOpShuffleProvider") + static void RearrangeFloat16Vector256Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i)).intoArray(r, i); + } + } + + assertRearrangeArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpShuffleMaskProvider") + static void RearrangeFloat16Vector256TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i), vmask).intoArray(r, i); + } + + assertRearrangeArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void compressFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.compress(vmask).intoArray(r, i); + } + } + + assertcompressArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void expandFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.expand(vmask).intoArray(r, i); + } + } + + assertexpandArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void getFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int num_lanes = SPECIES.length(); + // Manually unroll because full unroll happens after intrinsification. + // Unroll is needed because get intrinsic requires for index to be a known constant. + if (num_lanes == 1) { + r[i]=av.lane(0); + } else if (num_lanes == 2) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + } else if (num_lanes == 4) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + } else if (num_lanes == 8) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + } else if (num_lanes == 16) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + } else if (num_lanes == 32) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + } else if (num_lanes == 64) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + r[i+32]=av.lane(32); + r[i+33]=av.lane(33); + r[i+34]=av.lane(34); + r[i+35]=av.lane(35); + r[i+36]=av.lane(36); + r[i+37]=av.lane(37); + r[i+38]=av.lane(38); + r[i+39]=av.lane(39); + r[i+40]=av.lane(40); + r[i+41]=av.lane(41); + r[i+42]=av.lane(42); + r[i+43]=av.lane(43); + r[i+44]=av.lane(44); + r[i+45]=av.lane(45); + r[i+46]=av.lane(46); + r[i+47]=av.lane(47); + r[i+48]=av.lane(48); + r[i+49]=av.lane(49); + r[i+50]=av.lane(50); + r[i+51]=av.lane(51); + r[i+52]=av.lane(52); + r[i+53]=av.lane(53); + r[i+54]=av.lane(54); + r[i+55]=av.lane(55); + r[i+56]=av.lane(56); + r[i+57]=av.lane(57); + r[i+58]=av.lane(58); + r[i+59]=av.lane(59); + r[i+60]=av.lane(60); + r[i+61]=av.lane(61); + r[i+62]=av.lane(62); + r[i+63]=av.lane(63); + } else { + for (int j = 0; j < SPECIES.length(); j++) { + r[i+j]=av.lane(j); + } + } + } + } + + assertArraysStrictlyEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void BroadcastFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, a[i]).intoArray(r, i); + } + } + + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ZeroFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.zero(SPECIES).intoArray(a, i); + } + } + + assertEquals(a, r); + } + + static short[] sliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else + res[i] = (short)0; + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sliceUnaryFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.slice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector256Tests::sliceUnary); + } + + static short[] sliceBinary(short[] a, short[] b, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void sliceBinaryFloat16Vector256TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, Float16Vector256Tests::sliceBinary); + } + + static short[] slice(short[] a, short[] b, int origin, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = mask[i] ? a[idx+i+origin] : (short)0; + else { + res[i] = mask[i] ? b[idx+j] : (short)0; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void sliceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, mask, Float16Vector256Tests::slice); + } + + static short[] unsliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i < origin) + res[i] = (short)0; + else { + res[i] = a[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void unsliceUnaryFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.unslice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector256Tests::unsliceUnary); + } + + static short[] unsliceBinary(short[] a, short[] b, int origin, int part, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (part == 0) { + if (i < origin) + res[i] = b[idx+i]; + else { + res[i] = a[idx+j]; + j++; + } + } else if (part == 1) { + if (i < origin) + res[i] = a[idx+SPECIES.length()-origin+i]; + else { + res[i] = b[idx+origin+j]; + j++; + } + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void unsliceBinaryFloat16Vector256TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, Float16Vector256Tests::unsliceBinary); + } + + static short[] unslice(short[] a, short[] b, int origin, int part, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = b[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + for (int i = 0; i < SPECIES.length(); i++){ + res[i] = mask[i] ? a[idx+i] : res[i]; + } + short[] res1 = new short[SPECIES.length()]; + if (part == 0) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = b[idx+i]; + else { + res1[i] = res[j]; + j++; + } + } + } else if (part == 1) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = res[SPECIES.length()-origin+i]; + else { + res1[i] = b[idx+origin+j]; + j++; + } + } + } + return res1; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void unsliceFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, mask, Float16Vector256Tests::unslice); + } + + static short SIN(short a) { + return (short)(scalar_sin(a)); + } + + static short strictSIN(short a) { + return (short)(strict_scalar_sin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::SIN, Float16Vector256Tests::strictSIN); + } + + static short EXP(short a) { + return (short)(scalar_exp(a)); + } + + static short strictEXP(short a) { + return (short)(strict_scalar_exp(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXP).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::EXP, Float16Vector256Tests::strictEXP); + } + + static short LOG1P(short a) { + return (short)(scalar_log1p(a)); + } + + static short strictLOG1P(short a) { + return (short)(strict_scalar_log1p(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG1PFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG1P).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::LOG1P, Float16Vector256Tests::strictLOG1P); + } + + static short LOG(short a) { + return (short)(scalar_log(a)); + } + + static short strictLOG(short a) { + return (short)(strict_scalar_log(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOGFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::LOG, Float16Vector256Tests::strictLOG); + } + + static short LOG10(short a) { + return (short)(scalar_log10(a)); + } + + static short strictLOG10(short a) { + return (short)(strict_scalar_log10(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG10Float16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG10).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::LOG10, Float16Vector256Tests::strictLOG10); + } + + static short EXPM1(short a) { + return (short)(scalar_expm1(a)); + } + + static short strictEXPM1(short a) { + return (short)(strict_scalar_expm1(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPM1Float16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXPM1).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::EXPM1, Float16Vector256Tests::strictEXPM1); + } + + static short COS(short a) { + return (short)(scalar_cos(a)); + } + + static short strictCOS(short a) { + return (short)(strict_scalar_cos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::COS, Float16Vector256Tests::strictCOS); + } + + static short TAN(short a) { + return (short)(scalar_tan(a)); + } + + static short strictTAN(short a) { + return (short)(strict_scalar_tan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::TAN, Float16Vector256Tests::strictTAN); + } + + static short SINH(short a) { + return (short)(scalar_sinh(a)); + } + + static short strictSINH(short a) { + return (short)(strict_scalar_sinh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINHFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SINH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::SINH, Float16Vector256Tests::strictSINH); + } + + static short COSH(short a) { + return (short)(scalar_cosh(a)); + } + + static short strictCOSH(short a) { + return (short)(strict_scalar_cosh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSHFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COSH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::COSH, Float16Vector256Tests::strictCOSH); + } + + static short TANH(short a) { + return (short)(scalar_tanh(a)); + } + + static short strictTANH(short a) { + return (short)(strict_scalar_tanh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANHFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TANH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::TANH, Float16Vector256Tests::strictTANH); + } + + static short ASIN(short a) { + return (short)(scalar_asin(a)); + } + + static short strictASIN(short a) { + return (short)(strict_scalar_asin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ASINFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ASIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::ASIN, Float16Vector256Tests::strictASIN); + } + + static short ACOS(short a) { + return (short)(scalar_acos(a)); + } + + static short strictACOS(short a) { + return (short)(strict_scalar_acos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ACOSFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ACOS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::ACOS, Float16Vector256Tests::strictACOS); + } + + static short ATAN(short a) { + return (short)(scalar_atan(a)); + } + + static short strictATAN(short a) { + return (short)(strict_scalar_atan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ATANFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ATAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::ATAN, Float16Vector256Tests::strictATAN); + } + + static short CBRT(short a) { + return (short)(scalar_cbrt(a)); + } + + static short strictCBRT(short a) { + return (short)(strict_scalar_cbrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void CBRTFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.CBRT).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector256Tests::CBRT, Float16Vector256Tests::strictCBRT); + } + + static short HYPOT(short a, short b) { + return (short)(scalar_hypot(a, b)); + } + + static short strictHYPOT(short a, short b) { + return (short)(strict_scalar_hypot(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void HYPOTFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.HYPOT, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector256Tests::HYPOT, Float16Vector256Tests::strictHYPOT); + } + + + static short POW(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictPOW(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.POW, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector256Tests::POW, Float16Vector256Tests::strictPOW); + } + + + static short pow(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictpow(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.pow(bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector256Tests::pow, Float16Vector256Tests::strictpow); + } + + + static short ATAN2(short a, short b) { + return (short)(scalar_atan2(a, b)); + } + + static short strictATAN2(short a, short b) { + return (short)(strict_scalar_atan2(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ATAN2Float16Vector256Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ATAN2, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector256Tests::ATAN2, Float16Vector256Tests::strictATAN2); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.POW, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector256Tests::POW, Float16Vector256Tests::strictPOW); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.pow(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector256Tests::pow, Float16Vector256Tests::strictpow); + } + + + static short FMA(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + static short fma(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector256Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector256Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.fma(bv, cv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, c, Float16Vector256Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector256TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, mask, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i]).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a, b, c, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector256TestsAltBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv).intoArray(r, i); + } + assertAltBroadcastArraysEquals(r, a, b, c, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, c, mask, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector256TestsAltBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv, vmask).intoArray(r, i); + } + + assertAltBroadcastArraysEquals(r, a, b, c, mask, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector256TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector256Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector256TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.fma(b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector256Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector256TestsDoubleBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i], vmask).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, Float16Vector256Tests::FMA); + } + + static short NEG(short a) { + return (short)(scalar_neg((short)a)); + } + + static short neg(short a) { + return (short)(scalar_neg((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void NEGFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::NEG); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void negFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.neg().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::neg); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void NEGMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector256Tests::NEG); + } + + static short ABS(short a) { + return (short)(scalar_abs((short)a)); + } + + static short abs(short a) { + return (short)(scalar_abs((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ABSFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::ABS); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void absFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.abs().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::abs); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ABSMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector256Tests::ABS); + } + + static short SQRT(short a) { + return (short)(scalar_sqrt(a)); + } + + static short sqrt(short a) { + return (short)(scalar_sqrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SQRTFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::SQRT); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sqrtFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sqrt().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::sqrt); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void SQRTMaskedFloat16Vector256Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector256Tests::SQRT); + } + + static boolean band(boolean a, boolean b) { + return a & b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.and(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::band); + } + + static boolean bor(boolean a, boolean b) { + return a | b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskorFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.or(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::bor); + } + + static boolean bxor(boolean a, boolean b) { + return a != b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskxorFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.xor(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::bxor); + } + + static boolean bandNot(boolean a, boolean b) { + return a & !b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandNotFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.andNot(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::bandNot); + } + + static boolean beq(boolean a, boolean b) { + return a == b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskeqFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.eq(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector256Tests::beq); + } + + static boolean unot(boolean a) { + return !a; + } + + @Test(dataProvider = "boolMaskUnaryOpProvider") + static void masknotFloat16Vector256Tests(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + av.not().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector256Tests::unot); + } + + private static final long LONG_MASK_BITS = 0xFFFFFFFFFFFFFFFFL >>> (64 - SPECIES.length()); + + static void assertArraysEquals(long[] r, long[] a, long bits) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], a[i] & bits); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i] & bits, "(" + a[i] + ") at index #" + i); + } + } + + @Test(dataProvider = "longMaskProvider") + static void maskFromToLongFloat16Vector256Tests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = new long[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i++) { + VectorMask vmask = VectorMask.fromLong(SPECIES, a[i]); + r[i] = vmask.toLong(); + } + } + assertArraysEquals(r, a, LONG_MASK_BITS); + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector256TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.lt(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector256TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.eq(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shorttoIntUnaryOpProvider") + static void toIntArrayFloat16Vector256TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int[] r = av.toIntArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void toLongArrayFloat16Vector256TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + long[] r = av.toLongArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toDoubleArrayFloat16Vector256TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + double[] r = av.toDoubleArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toStringFloat16Vector256TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + String str = av.toString(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void hashCodeFloat16Vector256TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int hash = av.hashCode(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + + + static long ADDReduceLong(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return (long)res; + } + + static long ADDReduceAllLong(short[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLong(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceLongFloat16Vector256Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEquals(r, ra, a, + Float16Vector256Tests::ADDReduceLong, Float16Vector256Tests::ADDReduceAllLong); + } + + static long ADDReduceLongMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) { + res = scalar_add(res, a[i]); + } + } + + return (long)res; + } + + static long ADDReduceAllLongMasked(short[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLongMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceLongFloat16Vector256TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD, vmask); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEqualsMasked(r, ra, a, mask, + Float16Vector256Tests::ADDReduceLongMasked, Float16Vector256Tests::ADDReduceAllLongMasked); + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void BroadcastLongFloat16Vector256TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, shortBitsToFloat16(a[i]).longValue()).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector256TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.blend(shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + } + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector256Tests::blend); + } + + + @Test(dataProvider = "shortUnaryOpSelectFromProvider") + static void SelectFromFloat16Vector256Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat16Vector256Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector idxv = Float16Vector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") + static void SelectFromFloat16Vector256TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av, vmask).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleMiscellaneousFloat16Vector256TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + int hash = shuffle.hashCode(); + int length = shuffle.length(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + assertEquals(length, SPECIES.length()); + } + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleToStringFloat16Vector256TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + String str = shuffle.toString(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + Assert.assertTrue(str.equals("Shuffle" + Arrays.toString(subarr)), "at index " + + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + } + } + + @Test(dataProvider = "shuffleCompareOpProvider") + static void shuffleEqualsFloat16Vector256TestsSmokeTest(BiFunction fa, BiFunction fb) { + int[] a = fa.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + int[] b = fb.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = VectorShuffle.fromArray(SPECIES, a, i); + var bv = VectorShuffle.fromArray(SPECIES, b, i); + boolean eq = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(eq, Arrays.equals(a, i, to, b, i, to)); + } + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskEqualsFloat16Vector256Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = SPECIES.loadMask(a, i); + var bv = SPECIES.loadMask(b, i); + boolean equals = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(equals, Arrays.equals(a, i, to, b, i, to)); + } + } + } + + @Test(dataProvider = "maskProvider") + static void maskHashCodeFloat16Vector256TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + int hash = vmask.hashCode(); + + boolean subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static int maskTrueCount(boolean[] a, int idx) { + int trueCount = 0; + for (int i = idx; i < idx + SPECIES.length(); i++) { + trueCount += a[i] ? 1 : 0; + } + return trueCount; + } + + @Test(dataProvider = "maskProvider") + static void maskTrueCountFloat16Vector256TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.trueCount(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector256Tests::maskTrueCount); + } + + static int maskLastTrue(boolean[] a, int idx) { + int i = idx + SPECIES.length() - 1; + for (; i >= idx; i--) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskLastTrueFloat16Vector256TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.lastTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector256Tests::maskLastTrue); + } + + static int maskFirstTrue(boolean[] a, int idx) { + int i = idx; + for (; i < idx + SPECIES.length(); i++) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskFirstTrueFloat16Vector256TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.firstTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector256Tests::maskFirstTrue); + } + + @Test(dataProvider = "maskProvider") + static void maskCompressFloat16Vector256TestsSmokeTest(IntFunction fa) { + int trueCount = 0; + boolean[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + trueCount = vmask.trueCount(); + var rmask = vmask.compress(); + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(rmask.laneIsSet(j), j < trueCount); + } + } + } + } + + @DataProvider + public static Object[][] offsetProvider() { + return new Object[][]{ + {0}, + {-1}, + {+1}, + {+2}, + {-2}, + }; + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeFloat16Vector256TestsSmokeTest(int offset) { + int limit = SPECIES.length() * BUFFER_REPS; + for (int i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + int index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeLongFloat16Vector256TestsSmokeTest(int offset) { + long limit = SPECIES.length() * BUFFER_REPS; + for (long i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + long index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @DataProvider + public static Object[][] lengthProvider() { + return new Object[][]{ + {0}, + {1}, + {32}, + {37}, + {1024}, + {1024+1}, + {1024+5}, + }; + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundFloat16Vector256TestsSmokeTest(int length) { + int actualLoopBound = SPECIES.loopBound(length); + int expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundLongFloat16Vector256TestsSmokeTest(int _length) { + long length = _length; + long actualLoopBound = SPECIES.loopBound(length); + long expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test + static void ElementSizeFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + int elsize = av.elementSize(); + assertEquals(elsize, Float16.SIZE); + } + + @Test + static void VectorShapeFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + assert(vsh.equals(VectorShape.S_256_BIT)); + } + + @Test + static void ShapeWithLanesFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = vsh.withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void ElementTypeFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementType() == Float16.class); + } + + @Test + static void SpeciesElementSizeFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementSize() == Float16.SIZE); + } + + @Test + static void VectorTypeFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().vectorType() == av.getClass()); + } + + @Test + static void WithLanesFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorSpecies species = av.species().withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void WithShapeFloat16Vector256TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = av.species().withShape(vsh); + assert(species.equals(SPECIES)); + } + + @Test + static void MaskAllTrueFloat16Vector256TestsSmokeTest() { + for (int ic = 0; ic < INVOC_COUNT; ic++) { + assertEquals(SPECIES.maskAll(true).toLong(), -1L >>> (64 - SPECIES.length())); + } + } +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector512LoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float16Vector512LoadStoreTests.java new file mode 100644 index 00000000000..401baf67361 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector512LoadStoreTests.java @@ -0,0 +1,1038 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector java.base/jdk.internal.vm.annotation + * @run testng/othervm -XX:-TieredCompilation Float16Vector512LoadStoreTests + * + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.Arena; +import java.lang.foreign.ValueLayout; +import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.internal.vm.annotation.DontInline; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.ByteOrder; +import java.util.List; +import java.util.function.*; + +@Test +public class Float16Vector512LoadStoreTests extends AbstractVectorLoadStoreTest { + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_512; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); + + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0, "at index #" + i); + } + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("short[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i * 5)); + }), + withToString("short[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? 1 : (short)(i + 1))); + }) + ); + + // Relative to array.length + static final List> INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl + 1", (int l) -> { + return l - SPECIES.length() + 1; + }), + withToString("l + speciesl - 1", (int l) -> { + return l + SPECIES.length() - 1; + }), + withToString("l + speciesl", (int l) -> { + return l + SPECIES.length(); + }), + withToString("l + speciesl + 1", (int l) -> { + return l + SPECIES.length() + 1; + }) + ); + + // Relative to byte[] array.length or MemorySegment.byteSize() + static final List> BYTE_INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl*ebsize + 1", (int l) -> { + return l - SPECIES.vectorByteSize() + 1; + }), + withToString("l + speciesl*ebsize - 1", (int l) -> { + return l + SPECIES.vectorByteSize() - 1; + }), + withToString("l + speciesl*ebsize", (int l) -> { + return l + SPECIES.vectorByteSize(); + }), + withToString("l + speciesl*ebsize + 1", (int l) -> { + return l + SPECIES.vectorByteSize() + 1; + }) + ); + + @DataProvider + public Object[][] shortProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentProvider() { + return FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, bo}; + }))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, fm, bo}; + })))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + static MemorySegment toSegment(short[] a, IntFunction fb) { + MemorySegment ms = fb.apply(a.length * SPECIES.elementSize() / 8); + for (int i = 0; i < a.length; i++) { + ms.set(ELEMENT_LAYOUT, i * SPECIES.elementSize() / 8 , a[i]); + } + return ms; + } + + static short[] segmentToArray(MemorySegment ms) { + return ms.toArray(ELEMENT_LAYOUT); + } + + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + @DontInline + static VectorShuffle shuffleFromArray(int[] a, int i) { + return SPECIES.shuffleFromArray(a, i); + } + + @DontInline + static void shuffleIntoArray(VectorShuffle s, int[] a, int i) { + s.intoArray(a, i); + } + + @DontInline + static VectorShuffle shuffleFromMemorySegment(MemorySegment mem, int i, ByteOrder bo) { + return VectorShuffle.fromMemorySegment(SPECIES, mem, i, bo); + } + + @DontInline + static void shuffleIntoMemorySegment(VectorShuffle s, MemorySegment mem, int i, ByteOrder bo) { + s.intoMemorySegment(mem, i, bo); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromArray(a, i); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i, VectorMask m) { + return Float16Vector.fromArray(SPECIES, a, i, m); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i) { + v.intoArray(a, i); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i, VectorMask m) { + v.intoArray(a, i, m); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromMemorySegment(a, i, bo); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo, VectorMask m) { + return Float16Vector.fromMemorySegment(SPECIES, a, i, bo, m); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo) { + v.intoMemorySegment(a, i, bo); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo, VectorMask m) { + v.intoMemorySegment(a, i, bo, m); + } + + @Test(dataProvider = "shortProvider") + static void loadStoreArray(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i); + } + } + assertEquals(r, a); + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void loadArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + fromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void storeArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMaskArray(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, vmask); + av.intoArray(r, i); + } + } + assertArraysEquals(r, a, mask); + + + r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, vmask); + } + } + assertArraysEquals(r, a, mask); + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void loadArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i, vmask); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + fromArray(a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void storeArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i, vmask); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMask(IntFunction fa, + IntFunction fm) { + boolean[] mask = fm.apply(SPECIES.length()); + boolean[] r = new boolean[mask.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < mask.length; i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, mask); + } + + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo); + } + } + long m = r.mismatch(a); + Assert.assertEquals(m, -1, "Segments not equal"); + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder()); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentMaskProvider") + static void loadStoreMemorySegmentMask(IntFunction fa, + IntFunction fb, + IntFunction fm, + ByteOrder bo) { + short[] _a = fa.apply(SPECIES.length()); + MemorySegment a = toSegment(_a, fb); + MemorySegment r = fb.apply((int) a.byteSize()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo, vmask); + av.intoMemorySegment(r, i, bo); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + + + r = fb.apply((int) a.byteSize()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo, vmask); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder(), vmask); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder(), vmask); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreReadonlyMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb).asReadOnly(); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(true)) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(false)) + ); + + VectorMask m = SPECIES.shuffleFromOp(i -> i % 2 == 0 ? 1 : -1) + .laneIsValid(); + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, m) + ); + } + + + @Test(dataProvider = "maskProvider") + static void loadStoreMask(IntFunction fm) { + boolean[] a = fm.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask vmask = SPECIES.loadMask(a, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, a); + } + + + @Test(dataProvider = "shuffleIntProvider") + static void loadStoreShuffleArray(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = VectorShuffle.fromArray(SPECIES, a, i); + shuffle.intoArray(r, i); + } + } + + for (int i = 0; i < a.length; i++) { + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, a[i]), r[i]); + } + + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void storeShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffleIntoArray(shuffle, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + VectorShuffle shuffle = shuffleFromArray(a, index); + shuffleIntoArray(shuffle, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void loadShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffle.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + shuffleFromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntMemorySegmentProvider") + static void loadStoreShuffleMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; //An integer for every lane is read out. So 4 bytes per lane + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = VectorShuffle.fromMemorySegment(SPECIES, a, i, bo); + shuffle.intoMemorySegment(r, i, bo); + } + } + + for (int i = 0; i < l / 4; i++) { + int ai = a.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + int ri = r.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, ai), ri); + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleLoadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = shuffleFromMemorySegment(a, i, ByteOrder.nativeOrder()); + shuffle.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + shuffleFromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleStoreMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + + + // Gather/Scatter load/store tests + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], a[i + indexMap[j]]); + } + } + } catch (AssertionError e) { + assertEquals(r[j], a[i + indexMap[j]], "at index #" + j); + } + } + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0); + } + } + } catch (AssertionError e) { + assertEquals(r[i], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0, "at index #" + j); + } + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + if (mask[j % SPECIES.length()]) { + expected[i + indexMap[j]] = a[j]; + } + } + } + + assertEquals(r, expected); + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + expected[i + indexMap[j]] = a[j]; + } + } + + assertEquals(r, expected); + } + + @DataProvider + public Object[][] gatherScatterProvider() { + return INT_INDEX_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] gatherScatterMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fs -> INT_INDEX_GENERATORS.stream().flatMap(fm -> + FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm, fs}; + }))). + toArray(Object[][]::new); + } + + + @Test(dataProvider = "gatherScatterProvider") + static void gather(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void gatherMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i, vmask); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b, mask); + } + + @Test(dataProvider = "gatherScatterProvider") + static void scatter(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i); + } + } + + assertScatterArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void scatterMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i, vmask); + } + } + + assertScatterArraysEquals(r, a, b, mask); + } + + + +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java new file mode 100644 index 00000000000..3d2b7de23f9 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java @@ -0,0 +1,5898 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float16Vector512Tests + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import jdk.incubator.vector.VectorShape; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.Vector; + +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import jdk.incubator.vector.Float16Vector; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.Integer; +import java.util.List; +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Test +public class Float16Vector512Tests extends AbstractVectorTest { + + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_512; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + static void assertEquals(short actual, short expected, short delta) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta)); + } + static void assertEquals(short actual, short expected, short delta, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta), msg); + } + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + static void assertEquals(long actual, long expected) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected)); + } + static void assertEquals(long actual, long expected, String msg) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected), msg); + } + static void assertEquals(String actual, String expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(Object actual, Object expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + static void assertEquals(boolean actual, boolean expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(boolean actual, boolean expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + + + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + private static final short MIN_IDENTITY = float16ToRawShortBits(Float16.POSITIVE_INFINITY); + private static final short MUL_IDENTITY = float16ToRawShortBits(Float16.valueOf(1.0f)); + + // for floating point addition reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_ADD = float16ToRawShortBits(Float16.valueOf(10.0f)); + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_MUL = float16ToRawShortBits(Float16.valueOf(50.0f)); + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); + + static void assertArraysStrictlyEquals(short[] r, short[] a) { + for (int i = 0; i < a.length; i++) { + short ir = r[i]; + short ia = a[i]; + if (ir != ia) { + Assert.fail(String.format("at index #%d, expected = %016X, actual = %016X", i, ia, ir)); + } + } + } + + interface FUnOp { + short apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "at index #" + i + ", input = " + a[i]); + } + } + + interface FUnArrayOp { + short[] apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnArrayOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a[i])); + } + } catch (AssertionError e) { + short[] ref = f.apply(a[i]); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i], "at index #" + i + ", input = " + a[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + interface FReductionOp { + short apply(short[] a, int idx); + } + + interface FReductionAllOp { + short apply(short[] a); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (short)0.0); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa, + short relativeErrorFactor) { + int i = 0; + try { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor)))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor)))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor))), "at index #" + i); + } + } + + interface FReductionMaskedOp { + short apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOp { + short apply(short[] a, boolean[] mask); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (short)0.0); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + short relativeError) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError))))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError))))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError)))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError)))), "at index #" + i); + } + } + + interface FReductionOpLong { + long apply(short[] a, int idx); + } + + interface FReductionAllOpLong { + long apply(short[] a); + } + + static void assertReductionLongArraysEquals(long[] r, long rc, short[] a, + FReductionOpLong f, FReductionAllOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FReductionMaskedOpLong { + long apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOpLong { + long apply(short[] a, boolean[] mask); + } + + static void assertReductionLongArraysEqualsMasked(long[] r, long rc, short[] a, boolean[] mask, + FReductionMaskedOpLong f, FReductionAllMaskedOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + } + } + + interface FBoolReductionOp { + boolean apply(boolean[] a, int idx); + } + + static void assertReductionBoolArraysEquals(boolean[] r, boolean[] a, FBoolReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FMaskReductionOp { + int apply(boolean[] a, int idx); + } + + static void assertMaskReductionArraysEquals(int[] r, boolean[] a, FMaskReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+order[i+j]]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]]); + } + } + + static void assertcompressArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + k], a[i + j]); + k++; + } + } + for (; k < vector_len; k++) { + assertEquals(r[i + k], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + k; + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[idx], a[i + j], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + j], a[i + k]); + k++; + } else { + assertEquals(r[i + j], (short)0); + } + } + } + } catch (AssertionError e) { + int idx = i + j; + if (m[idx % SPECIES.length()]) { + assertEquals(r[idx], a[i + k], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod(shortBitsToFloat16(order[idx]).intValue(), 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a) { + int i = 0; + for (; i < a.length; i += SPECIES.length()) { + int idx = i; + for (int j = idx; j < (idx + SPECIES.length()); j++) + a[j]=a[idx]; + } + + try { + for (i = 0; i < a.length; i++) { + assertEquals(r[i], a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i], "at index #" + i + ", input = " + a[i]); + } + } + + interface FBoolUnOp { + boolean apply(boolean a); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, FBoolUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "(" + a[i] + ") at index #" + i); + } + } + + interface FBoolBinOp { + boolean apply(boolean a, boolean b); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, boolean[] b, FBoolBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + interface FBinOp { + short apply(short a, short b); + } + + interface FBinMaskOp { + short apply(short a, short b, boolean m); + + static FBinMaskOp lift(FBinOp f) { + return (a, b, m) -> m ? f.apply(a, b) : a; + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i])); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i]))); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i]), "left associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i])), "right associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinOp f) { + assertArraysEqualsAssociative(rl, rr, a, b, c, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinMaskOp f) { + int i = 0; + boolean mask_bit = false; + try { + for (; i < a.length; i++) { + mask_bit = mask[i % SPECIES.length()]; + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit)); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit)); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit), "left associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit), "right associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b), "(" + a[i] + ", " + b + ") at index #" + i); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())))); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue()))), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastLongArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j], b[j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertShiftArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", input2 = " + b[j] + ", mask = " + mask[i]); + } + } + + interface FBinConstOp { + short apply(short a); + } + + interface FBinConstMaskOp { + short apply(short a, boolean m); + + static FBinConstMaskOp lift(FBinConstOp f) { + return (a, m) -> m ? f.apply(a) : a; + } + } + + static void assertShiftConstEquals(short[] r, short[] a, FBinConstOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstOp f) { + assertShiftConstEquals(r, a, mask, FBinConstMaskOp.lift(f)); + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", mask = " + mask[i]); + } + } + + interface FTernOp { + short apply(short a, short b, short c); + } + + interface FTernMaskOp { + short apply(short a, short b, short c, boolean m); + + static FTernMaskOp lift(FTernOp f) { + return (a, b, c, m) -> m ? f.apply(a, b, c) : a; + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[i]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernOp f) { + assertArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + c[i]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertAltBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + + static boolean isWithin1Ulp(short actual, short expected) { + Float16 act = shortBitsToFloat16(actual); + Float16 exp = shortBitsToFloat16(expected); + if (Float16.isNaN(exp) && !Float16.isNaN(act)) { + return false; + } else if (!Float16.isNaN(exp) && Float16.isNaN(act)) { + return false; + } + + Float16 low = Float16.nextDown(exp); + Float16 high = Float16.nextUp(exp); + + if (Float16.compare(low, exp) > 0) { + return false; + } + + if (Float16.compare(high, exp) < 0) { + return false; + } + + return true; + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, FUnOp mathf, FUnOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i])), "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i])); + } + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i])), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], b[i])); + } + } + + static void assertBroadcastArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, + FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0 || + isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0, + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected = " + mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + Assert.assertTrue(isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])), + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], + b[(i / SPECIES.length()) * SPECIES.length()])); + } + } + + interface FGatherScatterOp { + short[] apply(short[] a, int ix, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, FGatherScatterOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + " at index #" + i); + } + } + + interface FGatherMaskedOp { + short[] apply(short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + interface FScatterMaskedOp { + short[] apply(short[] r, short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FGatherMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FScatterMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(r, a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(r, a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", r: " + + Arrays.toString(Arrays.copyOfRange(r, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + interface FLaneOp { + short[] apply(short[] a, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, int origin, FLaneOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + interface FLaneBop { + short[] apply(short[] a, short[] b, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, FLaneBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLaneMaskedBop { + short[] apply(short[] a, short[] b, int origin, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, boolean[] mask, FLaneMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLanePartBop { + short[] apply(short[] a, short[] b, int origin, int part, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, FLanePartBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + interface FLanePartMaskedBop { + short[] apply(short[] a, short[] b, int origin, int part, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, boolean[] mask, FLanePartMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + + static short convToFloat16(int i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convIntToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504); + case 1: + return convToFloat16(-65504); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> INT_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[intCornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convIntToFloat16CornerCases(i)); + }) + ); + + static void assertArraysEquals(int[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (int)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (int)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + + static short convToFloat16(long i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convLongToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504L); + case 1: + return convToFloat16(-65504L); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> LONG_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convLongToFloat16CornerCases(i)); + }) + ); + + + static void assertArraysEquals(long[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (long)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (long)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static void assertArraysEquals(double[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (double)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (double)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static short bits(short e) { + return e; + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01f + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (i % 17 == 0) ? cornerCaseValue(i) : Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + // Create combinations of pairs + // @@@ Might be sensitive to order e.g. div by 0 + static final List>> FLOAT16_GENERATOR_PAIRS = + Stream.of(FLOAT16_GENERATORS.get(0)). + flatMap(fa -> FLOAT16_GENERATORS.stream().skip(1).map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] boolUnaryOpProvider() { + return BOOL_ARRAY_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_GENERATOR_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> FLOAT16_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float16[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT16_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortBinaryOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortIndexedOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortBinaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpProvider() { + return FLOAT16_GENERATOR_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return FLOAT16_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_TRIPLES.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoIntUnaryOpProvider() { + return INT_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoLongUnaryOpProvider() { + return LONG_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longMaskProvider() { + return LONG_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskBinaryOpProvider() { + return BOOLEAN_MASK_COMPARE_GENERATOR_PAIRS.stream(). + map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskUnaryOpProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleCompareOpProvider() { + return INT_SHUFFLE_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_SHUFFLE_GENERATORS = List.of( + withToStringBi("shuffle[random]", (Integer l, Integer m) -> { + short[] a = new short[l]; + int upper = m; + for (int i = 0; i < 1; i++) { + a[i] = (short)RAND.nextInt(upper); + } + return a; + }) + ); + + @DataProvider + public Object[][] shortUnaryOpSelectFromProvider() { + return FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpSelectFromMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_COMPARE_GENERATORS = List.of( + withToString("float16[i]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)i); + }), + withToString("float16[i - length / 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - (s * BUFFER_REPS / 2))); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i + 1)); + }), + withToString("float16[i - 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - 2)); + }), + withToString("float16[zigZag(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i%3 == 0 ? (short)i : (i%3 == 1 ? (short)(i + 1) : (short)(i - 2))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + static final List>> FLOAT16_TEST_GENERATOR_ARGS = + FLOAT16_COMPARE_GENERATORS.stream(). + map(fa -> List.of(fa)). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortTestOpProvider() { + return FLOAT16_TEST_GENERATOR_ARGS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTestOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_TEST_GENERATOR_ARGS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_COMPARE_GENERATOR_PAIRS = + FLOAT16_COMPARE_GENERATORS.stream(). + flatMap(fa -> FLOAT16_COMPARE_GENERATORS.stream().map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortCompareOpProvider() { + return FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortCompareOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + static short cornerCaseValue(int i) { + return switch(i % 10) { + case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); + case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); + case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); + case 4 -> float16ToRawShortBits(Float16.NaN); + case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); + default -> float16ToShortBits(Float16.valueOf(-0.0f)); + }; + } + + static final IntFunction fr = (vl) -> { + int length = BUFFER_REPS * vl; + return new short[length]; + }; + + static final IntFunction fmr = (vl) -> { + int length = BUFFER_REPS * vl; + return new boolean[length]; + }; + + static final IntFunction lfr = (vl) -> { + int length = BUFFER_REPS * vl; + return new long[length]; + }; + + static boolean eq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() == bt.floatValue(); + } + + static boolean neq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() != bt.floatValue(); + } + + static boolean lt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() < bt.floatValue(); + } + + static boolean le(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() <= bt.floatValue(); + } + + static boolean gt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() > bt.floatValue(); + } + + static boolean ge(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() >= bt.floatValue(); + } + + static short firstNonZero(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 zero = shortBitsToFloat16((short)0); + return Float16.compare(at, zero) != 0 ? a : b; + } + + static short scalar_add(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.add(at, bt)); + } + + static short scalar_sub(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.subtract(at, bt)); + } + + static short scalar_mul(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.multiply(at, bt)); + + } + static short scalar_max(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.max(at, bt)); + } + + static short scalar_min(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.min(at, bt)); + } + + static short scalar_div(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.divide(at, bt)); + } + + static short scalar_fma(short a, short b, short c) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 ct = shortBitsToFloat16(c); + return float16ToRawShortBits(Float16.fma(at, bt, ct)); + } + + static short scalar_abs(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.abs(at)); + } + + static short scalar_neg(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.negate(at)); + } + + static short scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static boolean isNaN(short a) { + return Float16.isNaN(shortBitsToFloat16(a)); + } + static boolean isFinite(short a) { + return Float16.isFinite(shortBitsToFloat16(a)); + } + static boolean isInfinite(short a) { + return Float16.isInfinite(shortBitsToFloat16(a)); + } + + @Test + static void smokeTest1() { + Float16Vector three = Float16Vector.broadcast(SPECIES, float16ToRawShortBits(Float16.valueOf(-3))); + Float16Vector three2 = (Float16Vector) SPECIES.broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three2).allTrue()); + Float16Vector three3 = three2.broadcast(float16ToRawShortBits(Float16.valueOf(1))).broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three3).allTrue()); + int scale = 2; + Class ETYPE = short.class; + if (ETYPE == double.class || ETYPE == long.class) + scale = 1000000; + else if (ETYPE == byte.class && SPECIES.length() >= 64) + scale = 1; + Float16Vector higher = three.addIndex(scale); + VectorMask m = three.compare(VectorOperators.LE, higher); + assert(m.allTrue()); + m = higher.min(float16ToRawShortBits(Float16.valueOf(-1))).test(VectorOperators.IS_NEGATIVE); + assert(m.allTrue()); + m = higher.test(VectorOperators.IS_FINITE); + assert(m.allTrue()); + short max = higher.reduceLanes(VectorOperators.MAX); + assert(max == float16ToRawShortBits(Float16.add(Float16.valueOf(-3), Float16.multiply(Float16.valueOf(scale), Float16.valueOf((SPECIES.length()-1)))))); + } + + private static short[] + bothToArray(Float16Vector a, Float16Vector b) { + short[] r = new short[a.length() + b.length()]; + a.intoArray(r, 0); + b.intoArray(r, a.length()); + return r; + } + + @Test + static void smokeTest2() { + // Do some zipping and shuffling. + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES,0,1,false).toVector(); + assertEquals(io, io2); + Float16Vector a = io.add((short)1); //[1,2] + Float16Vector b = a.neg(); //[-1,-2] + short[] abValues = bothToArray(a,b); //[1,2,-1,-2] + VectorShuffle zip0 = VectorShuffle.makeZip(SPECIES, 0); + VectorShuffle zip1 = VectorShuffle.makeZip(SPECIES, 1); + Float16Vector zab0 = a.rearrange(zip0,b); //[1,-1] + Float16Vector zab1 = a.rearrange(zip1,b); //[2,-2] + short[] zabValues = bothToArray(zab0, zab1); //[1,-1,2,-2] + // manually zip + short[] manual = new short[zabValues.length]; + for (int i = 0; i < manual.length; i += 2) { + manual[i+0] = abValues[i/2]; + manual[i+1] = abValues[a.length() + i/2]; + } + assertEquals(Arrays.toString(zabValues), Arrays.toString(manual)); + VectorShuffle unz0 = VectorShuffle.makeUnzip(SPECIES, 0); + VectorShuffle unz1 = VectorShuffle.makeUnzip(SPECIES, 1); + Float16Vector uab0 = zab0.rearrange(unz0,zab1); + Float16Vector uab1 = zab0.rearrange(unz1,zab1); + short[] abValues1 = bothToArray(uab0, uab1); + assertEquals(Arrays.toString(abValues), Arrays.toString(abValues1)); + } + + static void iotaShuffle() { + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES, 0 , 1, false).toVector(); + assertEquals(io, io2); + } + + @Test + // Test all shuffle related operations. + static void shuffleTest() { + // To test backend instructions, make sure that C2 is used. + for (int loop = 0; loop < INVOC_COUNT * INVOC_COUNT; loop++) { + iotaShuffle(); + } + } + + @Test + void viewAsIntegeralLanesTest() { + Vector asIntegral = SPECIES.zero().viewAsIntegralLanes(); + VectorSpecies asIntegralSpecies = asIntegral.species(); + Assert.assertNotEquals(asIntegralSpecies.elementType(), SPECIES.elementType()); + assertEquals(asIntegralSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asIntegralSpecies.length(), SPECIES.length()); + assertEquals(asIntegral.viewAsFloatingLanes().species(), SPECIES); + } + + @Test + void viewAsFloatingLanesTest() { + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + assertEquals(asFloating.species(), SPECIES); + } + + static short ADD(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::ADD); + } + + static short add(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::add); + } + + static short SUB(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void SUBFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::SUB); + } + + static short sub(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void SUBFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::SUB); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::sub); + } + + static short MUL(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MULFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::MUL); + } + + static short mul(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void MULFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::MUL); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::mul); + } + + static short DIV(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void DIVFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::DIV); + } + + static short div(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void DIVFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::DIV); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::div); + } + + static short FIRST_NONZERO(short a, short b) { + return (short)(firstNonZero(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void FIRST_NONZEROFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void FIRST_NONZEROFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector512Tests::add); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector512Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector512Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector512Tests::div); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector512TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue()).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, Float16Vector512Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector512TestsBroadcastMaskedLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector512Tests::ADD); + } + + static Float16Vector bv_MIN = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINFloat16Vector512TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector512Tests::MIN); + } + + static Float16Vector bv_min = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void minFloat16Vector512TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(bv_min).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector512Tests::min); + } + + static Float16Vector bv_MIN_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINFloat16Vector512TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector512Tests::MIN); + } + + static Float16Vector bv_MAX = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXFloat16Vector512TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector512Tests::MAX); + } + + static Float16Vector bv_max = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void maxFloat16Vector512TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(bv_max).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector512Tests::max); + } + + static Float16Vector bv_MAX_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXFloat16Vector512TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector512Tests::MAX); + } + + static short MIN(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MIN, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::MIN); + } + + static short min(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.min(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::min); + } + + static short MAX(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MAX, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::MAX); + } + + static short max(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.max(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::max); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::MIN); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::min); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::MAX); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector512Tests::max); + } + + static short ADDReduce(short[] a, int idx) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAll(short[] a) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector512Tests::ADDReduce, Float16Vector512Tests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + assertEquals((short) (scalar_add(id, id)), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_add(id, x)), x); + assertEquals((short) (scalar_add(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_add(id, x)), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_add(x, id)), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAllMasked(short[] a, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector512Tests::ADDReduceMasked, Float16Vector512Tests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + static short MULReduce(short[] a, int idx) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAll(short[] a) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector512Tests::MULReduce, Float16Vector512Tests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + assertEquals((short) (scalar_mul(id, id)), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_mul(id, x)), x); + assertEquals((short) (scalar_mul(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_mul(id, x)), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_mul(x, id)), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAllMasked(short[] a, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MULReduceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector512Tests::MULReduceMasked, Float16Vector512Tests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + static short MINReduce(short[] a, int idx) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAll(short[] a) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector512Tests::MINReduce, Float16Vector512Tests::MINReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + assertEquals(scalar_min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_min(id, x), x); + assertEquals(scalar_min(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAllMasked(short[] a, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINReduceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector512Tests::MINReduceMasked, Float16Vector512Tests::MINReduceAllMasked); + } + + static short MAXReduce(short[] a, int idx) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAll(short[] a) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector512Tests::MAXReduce, Float16Vector512Tests::MAXReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + assertEquals(scalar_max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_max(id, x), x); + assertEquals(scalar_max(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAllMasked(short[] a, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXReduceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector512Tests::MAXReduceMasked, Float16Vector512Tests::MAXReduceAllMasked); + } + + static short FIRST_NONZEROReduce(short[] a, int idx) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAll(short[] a) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector512Tests::FIRST_NONZEROReduce, Float16Vector512Tests::FIRST_NONZEROReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(firstNonZero(id, x), x); + assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void FIRST_NONZEROReduceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector512Tests::FIRST_NONZEROReduceMasked, Float16Vector512Tests::FIRST_NONZEROReduceAllMasked); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void withFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0, j = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.withLane(j, b[i + j]).intoArray(r, i); + a[i + j] = b[i + j]; + j = (j + 1) & (SPECIES.length() - 1); + } + } + + + assertArraysStrictlyEquals(r, a); + } + + static boolean testIS_DEFAULT(short a) { + return bits(a)==0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_DEFAULTFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_DEFAULT(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_DEFAULTMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_DEFAULT(a[i + j])); + } + } + } + } + + static boolean testIS_NEGATIVE(short a) { + return bits(a)<0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NEGATIVEFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NEGATIVE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NEGATIVEMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NEGATIVE(a[i + j])); + } + } + } + } + + static boolean testIS_FINITE(short a) { + return isFinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_FINITEFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_FINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_FINITEMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_FINITE(a[i + j])); + } + } + } + } + + static boolean testIS_NAN(short a) { + return isNaN(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NANFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NAN(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NANMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NAN(a[i + j])); + } + } + } + } + + static boolean testIS_INFINITE(short a) { + return isInfinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_INFINITEFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_INFINITEMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.lt(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GTFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GTFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.eq(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void NEFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void NEFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LEFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LEFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GEFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GEFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector512TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector512TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector512TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector512TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + static short blend(short a, short b, boolean mask) { + return mask ? b : a; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector512Tests(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.blend(bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector512Tests::blend); + } + + @Test(dataProvider = "shortUnaryOpShuffleProvider") + static void RearrangeFloat16Vector512Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i)).intoArray(r, i); + } + } + + assertRearrangeArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpShuffleMaskProvider") + static void RearrangeFloat16Vector512TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i), vmask).intoArray(r, i); + } + + assertRearrangeArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void compressFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.compress(vmask).intoArray(r, i); + } + } + + assertcompressArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void expandFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.expand(vmask).intoArray(r, i); + } + } + + assertexpandArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void getFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int num_lanes = SPECIES.length(); + // Manually unroll because full unroll happens after intrinsification. + // Unroll is needed because get intrinsic requires for index to be a known constant. + if (num_lanes == 1) { + r[i]=av.lane(0); + } else if (num_lanes == 2) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + } else if (num_lanes == 4) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + } else if (num_lanes == 8) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + } else if (num_lanes == 16) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + } else if (num_lanes == 32) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + } else if (num_lanes == 64) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + r[i+32]=av.lane(32); + r[i+33]=av.lane(33); + r[i+34]=av.lane(34); + r[i+35]=av.lane(35); + r[i+36]=av.lane(36); + r[i+37]=av.lane(37); + r[i+38]=av.lane(38); + r[i+39]=av.lane(39); + r[i+40]=av.lane(40); + r[i+41]=av.lane(41); + r[i+42]=av.lane(42); + r[i+43]=av.lane(43); + r[i+44]=av.lane(44); + r[i+45]=av.lane(45); + r[i+46]=av.lane(46); + r[i+47]=av.lane(47); + r[i+48]=av.lane(48); + r[i+49]=av.lane(49); + r[i+50]=av.lane(50); + r[i+51]=av.lane(51); + r[i+52]=av.lane(52); + r[i+53]=av.lane(53); + r[i+54]=av.lane(54); + r[i+55]=av.lane(55); + r[i+56]=av.lane(56); + r[i+57]=av.lane(57); + r[i+58]=av.lane(58); + r[i+59]=av.lane(59); + r[i+60]=av.lane(60); + r[i+61]=av.lane(61); + r[i+62]=av.lane(62); + r[i+63]=av.lane(63); + } else { + for (int j = 0; j < SPECIES.length(); j++) { + r[i+j]=av.lane(j); + } + } + } + } + + assertArraysStrictlyEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void BroadcastFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, a[i]).intoArray(r, i); + } + } + + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ZeroFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.zero(SPECIES).intoArray(a, i); + } + } + + assertEquals(a, r); + } + + static short[] sliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else + res[i] = (short)0; + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sliceUnaryFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.slice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector512Tests::sliceUnary); + } + + static short[] sliceBinary(short[] a, short[] b, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void sliceBinaryFloat16Vector512TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, Float16Vector512Tests::sliceBinary); + } + + static short[] slice(short[] a, short[] b, int origin, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = mask[i] ? a[idx+i+origin] : (short)0; + else { + res[i] = mask[i] ? b[idx+j] : (short)0; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void sliceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, mask, Float16Vector512Tests::slice); + } + + static short[] unsliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i < origin) + res[i] = (short)0; + else { + res[i] = a[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void unsliceUnaryFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.unslice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector512Tests::unsliceUnary); + } + + static short[] unsliceBinary(short[] a, short[] b, int origin, int part, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (part == 0) { + if (i < origin) + res[i] = b[idx+i]; + else { + res[i] = a[idx+j]; + j++; + } + } else if (part == 1) { + if (i < origin) + res[i] = a[idx+SPECIES.length()-origin+i]; + else { + res[i] = b[idx+origin+j]; + j++; + } + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void unsliceBinaryFloat16Vector512TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, Float16Vector512Tests::unsliceBinary); + } + + static short[] unslice(short[] a, short[] b, int origin, int part, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = b[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + for (int i = 0; i < SPECIES.length(); i++){ + res[i] = mask[i] ? a[idx+i] : res[i]; + } + short[] res1 = new short[SPECIES.length()]; + if (part == 0) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = b[idx+i]; + else { + res1[i] = res[j]; + j++; + } + } + } else if (part == 1) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = res[SPECIES.length()-origin+i]; + else { + res1[i] = b[idx+origin+j]; + j++; + } + } + } + return res1; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void unsliceFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, mask, Float16Vector512Tests::unslice); + } + + static short SIN(short a) { + return (short)(scalar_sin(a)); + } + + static short strictSIN(short a) { + return (short)(strict_scalar_sin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::SIN, Float16Vector512Tests::strictSIN); + } + + static short EXP(short a) { + return (short)(scalar_exp(a)); + } + + static short strictEXP(short a) { + return (short)(strict_scalar_exp(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXP).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::EXP, Float16Vector512Tests::strictEXP); + } + + static short LOG1P(short a) { + return (short)(scalar_log1p(a)); + } + + static short strictLOG1P(short a) { + return (short)(strict_scalar_log1p(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG1PFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG1P).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::LOG1P, Float16Vector512Tests::strictLOG1P); + } + + static short LOG(short a) { + return (short)(scalar_log(a)); + } + + static short strictLOG(short a) { + return (short)(strict_scalar_log(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOGFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::LOG, Float16Vector512Tests::strictLOG); + } + + static short LOG10(short a) { + return (short)(scalar_log10(a)); + } + + static short strictLOG10(short a) { + return (short)(strict_scalar_log10(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG10Float16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG10).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::LOG10, Float16Vector512Tests::strictLOG10); + } + + static short EXPM1(short a) { + return (short)(scalar_expm1(a)); + } + + static short strictEXPM1(short a) { + return (short)(strict_scalar_expm1(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPM1Float16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXPM1).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::EXPM1, Float16Vector512Tests::strictEXPM1); + } + + static short COS(short a) { + return (short)(scalar_cos(a)); + } + + static short strictCOS(short a) { + return (short)(strict_scalar_cos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::COS, Float16Vector512Tests::strictCOS); + } + + static short TAN(short a) { + return (short)(scalar_tan(a)); + } + + static short strictTAN(short a) { + return (short)(strict_scalar_tan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::TAN, Float16Vector512Tests::strictTAN); + } + + static short SINH(short a) { + return (short)(scalar_sinh(a)); + } + + static short strictSINH(short a) { + return (short)(strict_scalar_sinh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINHFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SINH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::SINH, Float16Vector512Tests::strictSINH); + } + + static short COSH(short a) { + return (short)(scalar_cosh(a)); + } + + static short strictCOSH(short a) { + return (short)(strict_scalar_cosh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSHFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COSH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::COSH, Float16Vector512Tests::strictCOSH); + } + + static short TANH(short a) { + return (short)(scalar_tanh(a)); + } + + static short strictTANH(short a) { + return (short)(strict_scalar_tanh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANHFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TANH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::TANH, Float16Vector512Tests::strictTANH); + } + + static short ASIN(short a) { + return (short)(scalar_asin(a)); + } + + static short strictASIN(short a) { + return (short)(strict_scalar_asin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ASINFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ASIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::ASIN, Float16Vector512Tests::strictASIN); + } + + static short ACOS(short a) { + return (short)(scalar_acos(a)); + } + + static short strictACOS(short a) { + return (short)(strict_scalar_acos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ACOSFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ACOS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::ACOS, Float16Vector512Tests::strictACOS); + } + + static short ATAN(short a) { + return (short)(scalar_atan(a)); + } + + static short strictATAN(short a) { + return (short)(strict_scalar_atan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ATANFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ATAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::ATAN, Float16Vector512Tests::strictATAN); + } + + static short CBRT(short a) { + return (short)(scalar_cbrt(a)); + } + + static short strictCBRT(short a) { + return (short)(strict_scalar_cbrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void CBRTFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.CBRT).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector512Tests::CBRT, Float16Vector512Tests::strictCBRT); + } + + static short HYPOT(short a, short b) { + return (short)(scalar_hypot(a, b)); + } + + static short strictHYPOT(short a, short b) { + return (short)(strict_scalar_hypot(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void HYPOTFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.HYPOT, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector512Tests::HYPOT, Float16Vector512Tests::strictHYPOT); + } + + + static short POW(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictPOW(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.POW, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector512Tests::POW, Float16Vector512Tests::strictPOW); + } + + + static short pow(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictpow(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.pow(bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector512Tests::pow, Float16Vector512Tests::strictpow); + } + + + static short ATAN2(short a, short b) { + return (short)(scalar_atan2(a, b)); + } + + static short strictATAN2(short a, short b) { + return (short)(strict_scalar_atan2(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ATAN2Float16Vector512Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ATAN2, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector512Tests::ATAN2, Float16Vector512Tests::strictATAN2); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.POW, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector512Tests::POW, Float16Vector512Tests::strictPOW); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.pow(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector512Tests::pow, Float16Vector512Tests::strictpow); + } + + + static short FMA(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + static short fma(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector512Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector512Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.fma(bv, cv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, c, Float16Vector512Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector512TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, mask, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i]).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a, b, c, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector512TestsAltBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv).intoArray(r, i); + } + assertAltBroadcastArraysEquals(r, a, b, c, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, c, mask, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector512TestsAltBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv, vmask).intoArray(r, i); + } + + assertAltBroadcastArraysEquals(r, a, b, c, mask, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector512TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector512Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector512TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.fma(b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector512Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector512TestsDoubleBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i], vmask).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, Float16Vector512Tests::FMA); + } + + static short NEG(short a) { + return (short)(scalar_neg((short)a)); + } + + static short neg(short a) { + return (short)(scalar_neg((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void NEGFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::NEG); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void negFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.neg().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::neg); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void NEGMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector512Tests::NEG); + } + + static short ABS(short a) { + return (short)(scalar_abs((short)a)); + } + + static short abs(short a) { + return (short)(scalar_abs((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ABSFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::ABS); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void absFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.abs().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::abs); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ABSMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector512Tests::ABS); + } + + static short SQRT(short a) { + return (short)(scalar_sqrt(a)); + } + + static short sqrt(short a) { + return (short)(scalar_sqrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SQRTFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::SQRT); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sqrtFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sqrt().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::sqrt); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void SQRTMaskedFloat16Vector512Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector512Tests::SQRT); + } + + static boolean band(boolean a, boolean b) { + return a & b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.and(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::band); + } + + static boolean bor(boolean a, boolean b) { + return a | b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskorFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.or(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::bor); + } + + static boolean bxor(boolean a, boolean b) { + return a != b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskxorFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.xor(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::bxor); + } + + static boolean bandNot(boolean a, boolean b) { + return a & !b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandNotFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.andNot(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::bandNot); + } + + static boolean beq(boolean a, boolean b) { + return a == b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskeqFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.eq(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector512Tests::beq); + } + + static boolean unot(boolean a) { + return !a; + } + + @Test(dataProvider = "boolMaskUnaryOpProvider") + static void masknotFloat16Vector512Tests(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + av.not().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector512Tests::unot); + } + + private static final long LONG_MASK_BITS = 0xFFFFFFFFFFFFFFFFL >>> (64 - SPECIES.length()); + + static void assertArraysEquals(long[] r, long[] a, long bits) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], a[i] & bits); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i] & bits, "(" + a[i] + ") at index #" + i); + } + } + + @Test(dataProvider = "longMaskProvider") + static void maskFromToLongFloat16Vector512Tests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = new long[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i++) { + VectorMask vmask = VectorMask.fromLong(SPECIES, a[i]); + r[i] = vmask.toLong(); + } + } + assertArraysEquals(r, a, LONG_MASK_BITS); + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector512TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.lt(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector512TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.eq(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shorttoIntUnaryOpProvider") + static void toIntArrayFloat16Vector512TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int[] r = av.toIntArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void toLongArrayFloat16Vector512TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + long[] r = av.toLongArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toDoubleArrayFloat16Vector512TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + double[] r = av.toDoubleArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toStringFloat16Vector512TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + String str = av.toString(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void hashCodeFloat16Vector512TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int hash = av.hashCode(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + + + static long ADDReduceLong(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return (long)res; + } + + static long ADDReduceAllLong(short[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLong(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceLongFloat16Vector512Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEquals(r, ra, a, + Float16Vector512Tests::ADDReduceLong, Float16Vector512Tests::ADDReduceAllLong); + } + + static long ADDReduceLongMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) { + res = scalar_add(res, a[i]); + } + } + + return (long)res; + } + + static long ADDReduceAllLongMasked(short[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLongMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceLongFloat16Vector512TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD, vmask); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEqualsMasked(r, ra, a, mask, + Float16Vector512Tests::ADDReduceLongMasked, Float16Vector512Tests::ADDReduceAllLongMasked); + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void BroadcastLongFloat16Vector512TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, shortBitsToFloat16(a[i]).longValue()).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector512TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.blend(shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + } + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector512Tests::blend); + } + + + @Test(dataProvider = "shortUnaryOpSelectFromProvider") + static void SelectFromFloat16Vector512Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat16Vector512Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector idxv = Float16Vector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") + static void SelectFromFloat16Vector512TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av, vmask).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleMiscellaneousFloat16Vector512TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + int hash = shuffle.hashCode(); + int length = shuffle.length(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + assertEquals(length, SPECIES.length()); + } + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleToStringFloat16Vector512TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + String str = shuffle.toString(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + Assert.assertTrue(str.equals("Shuffle" + Arrays.toString(subarr)), "at index " + + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + } + } + + @Test(dataProvider = "shuffleCompareOpProvider") + static void shuffleEqualsFloat16Vector512TestsSmokeTest(BiFunction fa, BiFunction fb) { + int[] a = fa.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + int[] b = fb.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = VectorShuffle.fromArray(SPECIES, a, i); + var bv = VectorShuffle.fromArray(SPECIES, b, i); + boolean eq = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(eq, Arrays.equals(a, i, to, b, i, to)); + } + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskEqualsFloat16Vector512Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = SPECIES.loadMask(a, i); + var bv = SPECIES.loadMask(b, i); + boolean equals = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(equals, Arrays.equals(a, i, to, b, i, to)); + } + } + } + + @Test(dataProvider = "maskProvider") + static void maskHashCodeFloat16Vector512TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + int hash = vmask.hashCode(); + + boolean subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static int maskTrueCount(boolean[] a, int idx) { + int trueCount = 0; + for (int i = idx; i < idx + SPECIES.length(); i++) { + trueCount += a[i] ? 1 : 0; + } + return trueCount; + } + + @Test(dataProvider = "maskProvider") + static void maskTrueCountFloat16Vector512TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.trueCount(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector512Tests::maskTrueCount); + } + + static int maskLastTrue(boolean[] a, int idx) { + int i = idx + SPECIES.length() - 1; + for (; i >= idx; i--) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskLastTrueFloat16Vector512TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.lastTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector512Tests::maskLastTrue); + } + + static int maskFirstTrue(boolean[] a, int idx) { + int i = idx; + for (; i < idx + SPECIES.length(); i++) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskFirstTrueFloat16Vector512TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.firstTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector512Tests::maskFirstTrue); + } + + @Test(dataProvider = "maskProvider") + static void maskCompressFloat16Vector512TestsSmokeTest(IntFunction fa) { + int trueCount = 0; + boolean[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + trueCount = vmask.trueCount(); + var rmask = vmask.compress(); + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(rmask.laneIsSet(j), j < trueCount); + } + } + } + } + + @DataProvider + public static Object[][] offsetProvider() { + return new Object[][]{ + {0}, + {-1}, + {+1}, + {+2}, + {-2}, + }; + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeFloat16Vector512TestsSmokeTest(int offset) { + int limit = SPECIES.length() * BUFFER_REPS; + for (int i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + int index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeLongFloat16Vector512TestsSmokeTest(int offset) { + long limit = SPECIES.length() * BUFFER_REPS; + for (long i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + long index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @DataProvider + public static Object[][] lengthProvider() { + return new Object[][]{ + {0}, + {1}, + {32}, + {37}, + {1024}, + {1024+1}, + {1024+5}, + }; + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundFloat16Vector512TestsSmokeTest(int length) { + int actualLoopBound = SPECIES.loopBound(length); + int expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundLongFloat16Vector512TestsSmokeTest(int _length) { + long length = _length; + long actualLoopBound = SPECIES.loopBound(length); + long expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test + static void ElementSizeFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + int elsize = av.elementSize(); + assertEquals(elsize, Float16.SIZE); + } + + @Test + static void VectorShapeFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + assert(vsh.equals(VectorShape.S_512_BIT)); + } + + @Test + static void ShapeWithLanesFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = vsh.withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void ElementTypeFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementType() == Float16.class); + } + + @Test + static void SpeciesElementSizeFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementSize() == Float16.SIZE); + } + + @Test + static void VectorTypeFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().vectorType() == av.getClass()); + } + + @Test + static void WithLanesFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorSpecies species = av.species().withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void WithShapeFloat16Vector512TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = av.species().withShape(vsh); + assert(species.equals(SPECIES)); + } + + @Test + static void MaskAllTrueFloat16Vector512TestsSmokeTest() { + for (int ic = 0; ic < INVOC_COUNT; ic++) { + assertEquals(SPECIES.maskAll(true).toLong(), -1L >>> (64 - SPECIES.length())); + } + } +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector64LoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float16Vector64LoadStoreTests.java new file mode 100644 index 00000000000..83befc161a5 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector64LoadStoreTests.java @@ -0,0 +1,1038 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector java.base/jdk.internal.vm.annotation + * @run testng/othervm -XX:-TieredCompilation Float16Vector64LoadStoreTests + * + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.Arena; +import java.lang.foreign.ValueLayout; +import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.internal.vm.annotation.DontInline; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.ByteOrder; +import java.util.List; +import java.util.function.*; + +@Test +public class Float16Vector64LoadStoreTests extends AbstractVectorLoadStoreTest { + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_64; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); + + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0, "at index #" + i); + } + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("short[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i * 5)); + }), + withToString("short[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? 1 : (short)(i + 1))); + }) + ); + + // Relative to array.length + static final List> INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl + 1", (int l) -> { + return l - SPECIES.length() + 1; + }), + withToString("l + speciesl - 1", (int l) -> { + return l + SPECIES.length() - 1; + }), + withToString("l + speciesl", (int l) -> { + return l + SPECIES.length(); + }), + withToString("l + speciesl + 1", (int l) -> { + return l + SPECIES.length() + 1; + }) + ); + + // Relative to byte[] array.length or MemorySegment.byteSize() + static final List> BYTE_INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl*ebsize + 1", (int l) -> { + return l - SPECIES.vectorByteSize() + 1; + }), + withToString("l + speciesl*ebsize - 1", (int l) -> { + return l + SPECIES.vectorByteSize() - 1; + }), + withToString("l + speciesl*ebsize", (int l) -> { + return l + SPECIES.vectorByteSize(); + }), + withToString("l + speciesl*ebsize + 1", (int l) -> { + return l + SPECIES.vectorByteSize() + 1; + }) + ); + + @DataProvider + public Object[][] shortProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentProvider() { + return FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, bo}; + }))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, fm, bo}; + })))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + static MemorySegment toSegment(short[] a, IntFunction fb) { + MemorySegment ms = fb.apply(a.length * SPECIES.elementSize() / 8); + for (int i = 0; i < a.length; i++) { + ms.set(ELEMENT_LAYOUT, i * SPECIES.elementSize() / 8 , a[i]); + } + return ms; + } + + static short[] segmentToArray(MemorySegment ms) { + return ms.toArray(ELEMENT_LAYOUT); + } + + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + @DontInline + static VectorShuffle shuffleFromArray(int[] a, int i) { + return SPECIES.shuffleFromArray(a, i); + } + + @DontInline + static void shuffleIntoArray(VectorShuffle s, int[] a, int i) { + s.intoArray(a, i); + } + + @DontInline + static VectorShuffle shuffleFromMemorySegment(MemorySegment mem, int i, ByteOrder bo) { + return VectorShuffle.fromMemorySegment(SPECIES, mem, i, bo); + } + + @DontInline + static void shuffleIntoMemorySegment(VectorShuffle s, MemorySegment mem, int i, ByteOrder bo) { + s.intoMemorySegment(mem, i, bo); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromArray(a, i); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i, VectorMask m) { + return Float16Vector.fromArray(SPECIES, a, i, m); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i) { + v.intoArray(a, i); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i, VectorMask m) { + v.intoArray(a, i, m); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromMemorySegment(a, i, bo); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo, VectorMask m) { + return Float16Vector.fromMemorySegment(SPECIES, a, i, bo, m); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo) { + v.intoMemorySegment(a, i, bo); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo, VectorMask m) { + v.intoMemorySegment(a, i, bo, m); + } + + @Test(dataProvider = "shortProvider") + static void loadStoreArray(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i); + } + } + assertEquals(r, a); + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void loadArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + fromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void storeArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMaskArray(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, vmask); + av.intoArray(r, i); + } + } + assertArraysEquals(r, a, mask); + + + r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, vmask); + } + } + assertArraysEquals(r, a, mask); + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void loadArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i, vmask); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + fromArray(a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void storeArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i, vmask); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMask(IntFunction fa, + IntFunction fm) { + boolean[] mask = fm.apply(SPECIES.length()); + boolean[] r = new boolean[mask.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < mask.length; i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, mask); + } + + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo); + } + } + long m = r.mismatch(a); + Assert.assertEquals(m, -1, "Segments not equal"); + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder()); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentMaskProvider") + static void loadStoreMemorySegmentMask(IntFunction fa, + IntFunction fb, + IntFunction fm, + ByteOrder bo) { + short[] _a = fa.apply(SPECIES.length()); + MemorySegment a = toSegment(_a, fb); + MemorySegment r = fb.apply((int) a.byteSize()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo, vmask); + av.intoMemorySegment(r, i, bo); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + + + r = fb.apply((int) a.byteSize()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo, vmask); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder(), vmask); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder(), vmask); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreReadonlyMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb).asReadOnly(); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(true)) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(false)) + ); + + VectorMask m = SPECIES.shuffleFromOp(i -> i % 2 == 0 ? 1 : -1) + .laneIsValid(); + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, m) + ); + } + + + @Test(dataProvider = "maskProvider") + static void loadStoreMask(IntFunction fm) { + boolean[] a = fm.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask vmask = SPECIES.loadMask(a, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, a); + } + + + @Test(dataProvider = "shuffleIntProvider") + static void loadStoreShuffleArray(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = VectorShuffle.fromArray(SPECIES, a, i); + shuffle.intoArray(r, i); + } + } + + for (int i = 0; i < a.length; i++) { + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, a[i]), r[i]); + } + + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void storeShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffleIntoArray(shuffle, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + VectorShuffle shuffle = shuffleFromArray(a, index); + shuffleIntoArray(shuffle, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void loadShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffle.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + shuffleFromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntMemorySegmentProvider") + static void loadStoreShuffleMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; //An integer for every lane is read out. So 4 bytes per lane + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = VectorShuffle.fromMemorySegment(SPECIES, a, i, bo); + shuffle.intoMemorySegment(r, i, bo); + } + } + + for (int i = 0; i < l / 4; i++) { + int ai = a.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + int ri = r.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, ai), ri); + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleLoadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = shuffleFromMemorySegment(a, i, ByteOrder.nativeOrder()); + shuffle.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + shuffleFromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleStoreMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + + + // Gather/Scatter load/store tests + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], a[i + indexMap[j]]); + } + } + } catch (AssertionError e) { + assertEquals(r[j], a[i + indexMap[j]], "at index #" + j); + } + } + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0); + } + } + } catch (AssertionError e) { + assertEquals(r[i], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0, "at index #" + j); + } + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + if (mask[j % SPECIES.length()]) { + expected[i + indexMap[j]] = a[j]; + } + } + } + + assertEquals(r, expected); + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + expected[i + indexMap[j]] = a[j]; + } + } + + assertEquals(r, expected); + } + + @DataProvider + public Object[][] gatherScatterProvider() { + return INT_INDEX_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] gatherScatterMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fs -> INT_INDEX_GENERATORS.stream().flatMap(fm -> + FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm, fs}; + }))). + toArray(Object[][]::new); + } + + + @Test(dataProvider = "gatherScatterProvider") + static void gather(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void gatherMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i, vmask); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b, mask); + } + + @Test(dataProvider = "gatherScatterProvider") + static void scatter(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i); + } + } + + assertScatterArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void scatterMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i, vmask); + } + } + + assertScatterArraysEquals(r, a, b, mask); + } + + + +} diff --git a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java new file mode 100644 index 00000000000..c44f0f7c576 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java @@ -0,0 +1,5898 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float16Vector64Tests + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import jdk.incubator.vector.VectorShape; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.Vector; + +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import jdk.incubator.vector.Float16Vector; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.Integer; +import java.util.List; +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Test +public class Float16Vector64Tests extends AbstractVectorTest { + + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_64; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + static void assertEquals(short actual, short expected, short delta) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta)); + } + static void assertEquals(short actual, short expected, short delta, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta), msg); + } + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + static void assertEquals(long actual, long expected) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected)); + } + static void assertEquals(long actual, long expected, String msg) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected), msg); + } + static void assertEquals(String actual, String expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(Object actual, Object expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + static void assertEquals(boolean actual, boolean expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(boolean actual, boolean expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + + + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + private static final short MIN_IDENTITY = float16ToRawShortBits(Float16.POSITIVE_INFINITY); + private static final short MUL_IDENTITY = float16ToRawShortBits(Float16.valueOf(1.0f)); + + // for floating point addition reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_ADD = float16ToRawShortBits(Float16.valueOf(10.0f)); + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_MUL = float16ToRawShortBits(Float16.valueOf(50.0f)); + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); + + static void assertArraysStrictlyEquals(short[] r, short[] a) { + for (int i = 0; i < a.length; i++) { + short ir = r[i]; + short ia = a[i]; + if (ir != ia) { + Assert.fail(String.format("at index #%d, expected = %016X, actual = %016X", i, ia, ir)); + } + } + } + + interface FUnOp { + short apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "at index #" + i + ", input = " + a[i]); + } + } + + interface FUnArrayOp { + short[] apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnArrayOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a[i])); + } + } catch (AssertionError e) { + short[] ref = f.apply(a[i]); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i], "at index #" + i + ", input = " + a[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + interface FReductionOp { + short apply(short[] a, int idx); + } + + interface FReductionAllOp { + short apply(short[] a); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (short)0.0); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa, + short relativeErrorFactor) { + int i = 0; + try { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor)))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor)))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor))), "at index #" + i); + } + } + + interface FReductionMaskedOp { + short apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOp { + short apply(short[] a, boolean[] mask); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (short)0.0); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + short relativeError) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError))))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError))))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError)))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError)))), "at index #" + i); + } + } + + interface FReductionOpLong { + long apply(short[] a, int idx); + } + + interface FReductionAllOpLong { + long apply(short[] a); + } + + static void assertReductionLongArraysEquals(long[] r, long rc, short[] a, + FReductionOpLong f, FReductionAllOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FReductionMaskedOpLong { + long apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOpLong { + long apply(short[] a, boolean[] mask); + } + + static void assertReductionLongArraysEqualsMasked(long[] r, long rc, short[] a, boolean[] mask, + FReductionMaskedOpLong f, FReductionAllMaskedOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + } + } + + interface FBoolReductionOp { + boolean apply(boolean[] a, int idx); + } + + static void assertReductionBoolArraysEquals(boolean[] r, boolean[] a, FBoolReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FMaskReductionOp { + int apply(boolean[] a, int idx); + } + + static void assertMaskReductionArraysEquals(int[] r, boolean[] a, FMaskReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+order[i+j]]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]]); + } + } + + static void assertcompressArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + k], a[i + j]); + k++; + } + } + for (; k < vector_len; k++) { + assertEquals(r[i + k], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + k; + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[idx], a[i + j], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + j], a[i + k]); + k++; + } else { + assertEquals(r[i + j], (short)0); + } + } + } + } catch (AssertionError e) { + int idx = i + j; + if (m[idx % SPECIES.length()]) { + assertEquals(r[idx], a[i + k], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod(shortBitsToFloat16(order[idx]).intValue(), 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a) { + int i = 0; + for (; i < a.length; i += SPECIES.length()) { + int idx = i; + for (int j = idx; j < (idx + SPECIES.length()); j++) + a[j]=a[idx]; + } + + try { + for (i = 0; i < a.length; i++) { + assertEquals(r[i], a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i], "at index #" + i + ", input = " + a[i]); + } + } + + interface FBoolUnOp { + boolean apply(boolean a); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, FBoolUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "(" + a[i] + ") at index #" + i); + } + } + + interface FBoolBinOp { + boolean apply(boolean a, boolean b); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, boolean[] b, FBoolBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + interface FBinOp { + short apply(short a, short b); + } + + interface FBinMaskOp { + short apply(short a, short b, boolean m); + + static FBinMaskOp lift(FBinOp f) { + return (a, b, m) -> m ? f.apply(a, b) : a; + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i])); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i]))); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i]), "left associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i])), "right associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinOp f) { + assertArraysEqualsAssociative(rl, rr, a, b, c, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinMaskOp f) { + int i = 0; + boolean mask_bit = false; + try { + for (; i < a.length; i++) { + mask_bit = mask[i % SPECIES.length()]; + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit)); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit)); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit), "left associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit), "right associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b), "(" + a[i] + ", " + b + ") at index #" + i); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())))); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue()))), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastLongArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j], b[j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertShiftArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", input2 = " + b[j] + ", mask = " + mask[i]); + } + } + + interface FBinConstOp { + short apply(short a); + } + + interface FBinConstMaskOp { + short apply(short a, boolean m); + + static FBinConstMaskOp lift(FBinConstOp f) { + return (a, m) -> m ? f.apply(a) : a; + } + } + + static void assertShiftConstEquals(short[] r, short[] a, FBinConstOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstOp f) { + assertShiftConstEquals(r, a, mask, FBinConstMaskOp.lift(f)); + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", mask = " + mask[i]); + } + } + + interface FTernOp { + short apply(short a, short b, short c); + } + + interface FTernMaskOp { + short apply(short a, short b, short c, boolean m); + + static FTernMaskOp lift(FTernOp f) { + return (a, b, c, m) -> m ? f.apply(a, b, c) : a; + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[i]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernOp f) { + assertArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + c[i]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertAltBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + + static boolean isWithin1Ulp(short actual, short expected) { + Float16 act = shortBitsToFloat16(actual); + Float16 exp = shortBitsToFloat16(expected); + if (Float16.isNaN(exp) && !Float16.isNaN(act)) { + return false; + } else if (!Float16.isNaN(exp) && Float16.isNaN(act)) { + return false; + } + + Float16 low = Float16.nextDown(exp); + Float16 high = Float16.nextUp(exp); + + if (Float16.compare(low, exp) > 0) { + return false; + } + + if (Float16.compare(high, exp) < 0) { + return false; + } + + return true; + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, FUnOp mathf, FUnOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i])), "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i])); + } + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i])), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], b[i])); + } + } + + static void assertBroadcastArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, + FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0 || + isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0, + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected = " + mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + Assert.assertTrue(isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])), + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], + b[(i / SPECIES.length()) * SPECIES.length()])); + } + } + + interface FGatherScatterOp { + short[] apply(short[] a, int ix, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, FGatherScatterOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + " at index #" + i); + } + } + + interface FGatherMaskedOp { + short[] apply(short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + interface FScatterMaskedOp { + short[] apply(short[] r, short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FGatherMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FScatterMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(r, a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(r, a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", r: " + + Arrays.toString(Arrays.copyOfRange(r, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + interface FLaneOp { + short[] apply(short[] a, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, int origin, FLaneOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + interface FLaneBop { + short[] apply(short[] a, short[] b, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, FLaneBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLaneMaskedBop { + short[] apply(short[] a, short[] b, int origin, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, boolean[] mask, FLaneMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLanePartBop { + short[] apply(short[] a, short[] b, int origin, int part, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, FLanePartBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + interface FLanePartMaskedBop { + short[] apply(short[] a, short[] b, int origin, int part, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, boolean[] mask, FLanePartMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + + static short convToFloat16(int i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convIntToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504); + case 1: + return convToFloat16(-65504); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> INT_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[intCornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convIntToFloat16CornerCases(i)); + }) + ); + + static void assertArraysEquals(int[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (int)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (int)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + + static short convToFloat16(long i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convLongToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504L); + case 1: + return convToFloat16(-65504L); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> LONG_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convLongToFloat16CornerCases(i)); + }) + ); + + + static void assertArraysEquals(long[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (long)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (long)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static void assertArraysEquals(double[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (double)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (double)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static short bits(short e) { + return e; + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01f + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (i % 17 == 0) ? cornerCaseValue(i) : Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + // Create combinations of pairs + // @@@ Might be sensitive to order e.g. div by 0 + static final List>> FLOAT16_GENERATOR_PAIRS = + Stream.of(FLOAT16_GENERATORS.get(0)). + flatMap(fa -> FLOAT16_GENERATORS.stream().skip(1).map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] boolUnaryOpProvider() { + return BOOL_ARRAY_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_GENERATOR_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> FLOAT16_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float16[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT16_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortBinaryOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortIndexedOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortBinaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpProvider() { + return FLOAT16_GENERATOR_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return FLOAT16_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_TRIPLES.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoIntUnaryOpProvider() { + return INT_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoLongUnaryOpProvider() { + return LONG_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longMaskProvider() { + return LONG_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskBinaryOpProvider() { + return BOOLEAN_MASK_COMPARE_GENERATOR_PAIRS.stream(). + map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskUnaryOpProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleCompareOpProvider() { + return INT_SHUFFLE_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_SHUFFLE_GENERATORS = List.of( + withToStringBi("shuffle[random]", (Integer l, Integer m) -> { + short[] a = new short[l]; + int upper = m; + for (int i = 0; i < 1; i++) { + a[i] = (short)RAND.nextInt(upper); + } + return a; + }) + ); + + @DataProvider + public Object[][] shortUnaryOpSelectFromProvider() { + return FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpSelectFromMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_COMPARE_GENERATORS = List.of( + withToString("float16[i]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)i); + }), + withToString("float16[i - length / 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - (s * BUFFER_REPS / 2))); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i + 1)); + }), + withToString("float16[i - 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - 2)); + }), + withToString("float16[zigZag(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i%3 == 0 ? (short)i : (i%3 == 1 ? (short)(i + 1) : (short)(i - 2))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + static final List>> FLOAT16_TEST_GENERATOR_ARGS = + FLOAT16_COMPARE_GENERATORS.stream(). + map(fa -> List.of(fa)). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortTestOpProvider() { + return FLOAT16_TEST_GENERATOR_ARGS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTestOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_TEST_GENERATOR_ARGS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_COMPARE_GENERATOR_PAIRS = + FLOAT16_COMPARE_GENERATORS.stream(). + flatMap(fa -> FLOAT16_COMPARE_GENERATORS.stream().map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortCompareOpProvider() { + return FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortCompareOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + static short cornerCaseValue(int i) { + return switch(i % 10) { + case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); + case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); + case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); + case 4 -> float16ToRawShortBits(Float16.NaN); + case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); + default -> float16ToShortBits(Float16.valueOf(-0.0f)); + }; + } + + static final IntFunction fr = (vl) -> { + int length = BUFFER_REPS * vl; + return new short[length]; + }; + + static final IntFunction fmr = (vl) -> { + int length = BUFFER_REPS * vl; + return new boolean[length]; + }; + + static final IntFunction lfr = (vl) -> { + int length = BUFFER_REPS * vl; + return new long[length]; + }; + + static boolean eq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() == bt.floatValue(); + } + + static boolean neq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() != bt.floatValue(); + } + + static boolean lt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() < bt.floatValue(); + } + + static boolean le(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() <= bt.floatValue(); + } + + static boolean gt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() > bt.floatValue(); + } + + static boolean ge(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() >= bt.floatValue(); + } + + static short firstNonZero(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 zero = shortBitsToFloat16((short)0); + return Float16.compare(at, zero) != 0 ? a : b; + } + + static short scalar_add(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.add(at, bt)); + } + + static short scalar_sub(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.subtract(at, bt)); + } + + static short scalar_mul(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.multiply(at, bt)); + + } + static short scalar_max(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.max(at, bt)); + } + + static short scalar_min(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.min(at, bt)); + } + + static short scalar_div(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.divide(at, bt)); + } + + static short scalar_fma(short a, short b, short c) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 ct = shortBitsToFloat16(c); + return float16ToRawShortBits(Float16.fma(at, bt, ct)); + } + + static short scalar_abs(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.abs(at)); + } + + static short scalar_neg(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.negate(at)); + } + + static short scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static boolean isNaN(short a) { + return Float16.isNaN(shortBitsToFloat16(a)); + } + static boolean isFinite(short a) { + return Float16.isFinite(shortBitsToFloat16(a)); + } + static boolean isInfinite(short a) { + return Float16.isInfinite(shortBitsToFloat16(a)); + } + + @Test + static void smokeTest1() { + Float16Vector three = Float16Vector.broadcast(SPECIES, float16ToRawShortBits(Float16.valueOf(-3))); + Float16Vector three2 = (Float16Vector) SPECIES.broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three2).allTrue()); + Float16Vector three3 = three2.broadcast(float16ToRawShortBits(Float16.valueOf(1))).broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three3).allTrue()); + int scale = 2; + Class ETYPE = short.class; + if (ETYPE == double.class || ETYPE == long.class) + scale = 1000000; + else if (ETYPE == byte.class && SPECIES.length() >= 64) + scale = 1; + Float16Vector higher = three.addIndex(scale); + VectorMask m = three.compare(VectorOperators.LE, higher); + assert(m.allTrue()); + m = higher.min(float16ToRawShortBits(Float16.valueOf(-1))).test(VectorOperators.IS_NEGATIVE); + assert(m.allTrue()); + m = higher.test(VectorOperators.IS_FINITE); + assert(m.allTrue()); + short max = higher.reduceLanes(VectorOperators.MAX); + assert(max == float16ToRawShortBits(Float16.add(Float16.valueOf(-3), Float16.multiply(Float16.valueOf(scale), Float16.valueOf((SPECIES.length()-1)))))); + } + + private static short[] + bothToArray(Float16Vector a, Float16Vector b) { + short[] r = new short[a.length() + b.length()]; + a.intoArray(r, 0); + b.intoArray(r, a.length()); + return r; + } + + @Test + static void smokeTest2() { + // Do some zipping and shuffling. + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES,0,1,false).toVector(); + assertEquals(io, io2); + Float16Vector a = io.add((short)1); //[1,2] + Float16Vector b = a.neg(); //[-1,-2] + short[] abValues = bothToArray(a,b); //[1,2,-1,-2] + VectorShuffle zip0 = VectorShuffle.makeZip(SPECIES, 0); + VectorShuffle zip1 = VectorShuffle.makeZip(SPECIES, 1); + Float16Vector zab0 = a.rearrange(zip0,b); //[1,-1] + Float16Vector zab1 = a.rearrange(zip1,b); //[2,-2] + short[] zabValues = bothToArray(zab0, zab1); //[1,-1,2,-2] + // manually zip + short[] manual = new short[zabValues.length]; + for (int i = 0; i < manual.length; i += 2) { + manual[i+0] = abValues[i/2]; + manual[i+1] = abValues[a.length() + i/2]; + } + assertEquals(Arrays.toString(zabValues), Arrays.toString(manual)); + VectorShuffle unz0 = VectorShuffle.makeUnzip(SPECIES, 0); + VectorShuffle unz1 = VectorShuffle.makeUnzip(SPECIES, 1); + Float16Vector uab0 = zab0.rearrange(unz0,zab1); + Float16Vector uab1 = zab0.rearrange(unz1,zab1); + short[] abValues1 = bothToArray(uab0, uab1); + assertEquals(Arrays.toString(abValues), Arrays.toString(abValues1)); + } + + static void iotaShuffle() { + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES, 0 , 1, false).toVector(); + assertEquals(io, io2); + } + + @Test + // Test all shuffle related operations. + static void shuffleTest() { + // To test backend instructions, make sure that C2 is used. + for (int loop = 0; loop < INVOC_COUNT * INVOC_COUNT; loop++) { + iotaShuffle(); + } + } + + @Test + void viewAsIntegeralLanesTest() { + Vector asIntegral = SPECIES.zero().viewAsIntegralLanes(); + VectorSpecies asIntegralSpecies = asIntegral.species(); + Assert.assertNotEquals(asIntegralSpecies.elementType(), SPECIES.elementType()); + assertEquals(asIntegralSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asIntegralSpecies.length(), SPECIES.length()); + assertEquals(asIntegral.viewAsFloatingLanes().species(), SPECIES); + } + + @Test + void viewAsFloatingLanesTest() { + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + assertEquals(asFloating.species(), SPECIES); + } + + static short ADD(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::ADD); + } + + static short add(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::add); + } + + static short SUB(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void SUBFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::SUB); + } + + static short sub(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void SUBFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::SUB); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::sub); + } + + static short MUL(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MULFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::MUL); + } + + static short mul(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void MULFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::MUL); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::mul); + } + + static short DIV(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void DIVFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::DIV); + } + + static short div(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void DIVFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::DIV); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::div); + } + + static short FIRST_NONZERO(short a, short b) { + return (short)(firstNonZero(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void FIRST_NONZEROFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void FIRST_NONZEROFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector64Tests::add); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector64Tests::sub); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector64Tests::mul); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16Vector64Tests::div); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16Vector64TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue()).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, Float16Vector64Tests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16Vector64TestsBroadcastMaskedLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector64Tests::ADD); + } + + static Float16Vector bv_MIN = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINFloat16Vector64TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector64Tests::MIN); + } + + static Float16Vector bv_min = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void minFloat16Vector64TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(bv_min).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector64Tests::min); + } + + static Float16Vector bv_MIN_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINFloat16Vector64TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector64Tests::MIN); + } + + static Float16Vector bv_MAX = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXFloat16Vector64TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector64Tests::MAX); + } + + static Float16Vector bv_max = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void maxFloat16Vector64TestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(bv_max).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16Vector64Tests::max); + } + + static Float16Vector bv_MAX_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXFloat16Vector64TestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16Vector64Tests::MAX); + } + + static short MIN(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MIN, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::MIN); + } + + static short min(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.min(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::min); + } + + static short MAX(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MAX, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::MAX); + } + + static short max(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.max(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::max); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::MIN); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::min); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::MAX); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16Vector64Tests::max); + } + + static short ADDReduce(short[] a, int idx) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAll(short[] a) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector64Tests::ADDReduce, Float16Vector64Tests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + assertEquals((short) (scalar_add(id, id)), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_add(id, x)), x); + assertEquals((short) (scalar_add(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_add(id, x)), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_add(x, id)), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAllMasked(short[] a, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector64Tests::ADDReduceMasked, Float16Vector64Tests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + static short MULReduce(short[] a, int idx) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAll(short[] a) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector64Tests::MULReduce, Float16Vector64Tests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + assertEquals((short) (scalar_mul(id, id)), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_mul(id, x)), x); + assertEquals((short) (scalar_mul(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_mul(id, x)), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_mul(x, id)), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAllMasked(short[] a, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MULReduceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector64Tests::MULReduceMasked, Float16Vector64Tests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + static short MINReduce(short[] a, int idx) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAll(short[] a) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector64Tests::MINReduce, Float16Vector64Tests::MINReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + assertEquals(scalar_min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_min(id, x), x); + assertEquals(scalar_min(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAllMasked(short[] a, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINReduceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector64Tests::MINReduceMasked, Float16Vector64Tests::MINReduceAllMasked); + } + + static short MAXReduce(short[] a, int idx) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAll(short[] a) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector64Tests::MAXReduce, Float16Vector64Tests::MAXReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + assertEquals(scalar_max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_max(id, x), x); + assertEquals(scalar_max(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAllMasked(short[] a, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXReduceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector64Tests::MAXReduceMasked, Float16Vector64Tests::MAXReduceAllMasked); + } + + static short FIRST_NONZEROReduce(short[] a, int idx) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAll(short[] a) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16Vector64Tests::FIRST_NONZEROReduce, Float16Vector64Tests::FIRST_NONZEROReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(firstNonZero(id, x), x); + assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void FIRST_NONZEROReduceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16Vector64Tests::FIRST_NONZEROReduceMasked, Float16Vector64Tests::FIRST_NONZEROReduceAllMasked); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void withFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0, j = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.withLane(j, b[i + j]).intoArray(r, i); + a[i + j] = b[i + j]; + j = (j + 1) & (SPECIES.length() - 1); + } + } + + + assertArraysStrictlyEquals(r, a); + } + + static boolean testIS_DEFAULT(short a) { + return bits(a)==0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_DEFAULTFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_DEFAULT(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_DEFAULTMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_DEFAULT(a[i + j])); + } + } + } + } + + static boolean testIS_NEGATIVE(short a) { + return bits(a)<0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NEGATIVEFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NEGATIVE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NEGATIVEMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NEGATIVE(a[i + j])); + } + } + } + } + + static boolean testIS_FINITE(short a) { + return isFinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_FINITEFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_FINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_FINITEMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_FINITE(a[i + j])); + } + } + } + } + + static boolean testIS_NAN(short a) { + return isNaN(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NANFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NAN(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NANMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NAN(a[i + j])); + } + } + } + } + + static boolean testIS_INFINITE(short a) { + return isInfinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_INFINITEFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_INFINITEMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.lt(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GTFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GTFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.eq(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void NEFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void NEFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LEFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LEFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GEFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GEFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16Vector64TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16Vector64TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16Vector64TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16Vector64TestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + static short blend(short a, short b, boolean mask) { + return mask ? b : a; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector64Tests(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.blend(bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16Vector64Tests::blend); + } + + @Test(dataProvider = "shortUnaryOpShuffleProvider") + static void RearrangeFloat16Vector64Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i)).intoArray(r, i); + } + } + + assertRearrangeArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpShuffleMaskProvider") + static void RearrangeFloat16Vector64TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i), vmask).intoArray(r, i); + } + + assertRearrangeArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void compressFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.compress(vmask).intoArray(r, i); + } + } + + assertcompressArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void expandFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.expand(vmask).intoArray(r, i); + } + } + + assertexpandArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void getFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int num_lanes = SPECIES.length(); + // Manually unroll because full unroll happens after intrinsification. + // Unroll is needed because get intrinsic requires for index to be a known constant. + if (num_lanes == 1) { + r[i]=av.lane(0); + } else if (num_lanes == 2) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + } else if (num_lanes == 4) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + } else if (num_lanes == 8) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + } else if (num_lanes == 16) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + } else if (num_lanes == 32) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + } else if (num_lanes == 64) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + r[i+32]=av.lane(32); + r[i+33]=av.lane(33); + r[i+34]=av.lane(34); + r[i+35]=av.lane(35); + r[i+36]=av.lane(36); + r[i+37]=av.lane(37); + r[i+38]=av.lane(38); + r[i+39]=av.lane(39); + r[i+40]=av.lane(40); + r[i+41]=av.lane(41); + r[i+42]=av.lane(42); + r[i+43]=av.lane(43); + r[i+44]=av.lane(44); + r[i+45]=av.lane(45); + r[i+46]=av.lane(46); + r[i+47]=av.lane(47); + r[i+48]=av.lane(48); + r[i+49]=av.lane(49); + r[i+50]=av.lane(50); + r[i+51]=av.lane(51); + r[i+52]=av.lane(52); + r[i+53]=av.lane(53); + r[i+54]=av.lane(54); + r[i+55]=av.lane(55); + r[i+56]=av.lane(56); + r[i+57]=av.lane(57); + r[i+58]=av.lane(58); + r[i+59]=av.lane(59); + r[i+60]=av.lane(60); + r[i+61]=av.lane(61); + r[i+62]=av.lane(62); + r[i+63]=av.lane(63); + } else { + for (int j = 0; j < SPECIES.length(); j++) { + r[i+j]=av.lane(j); + } + } + } + } + + assertArraysStrictlyEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void BroadcastFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, a[i]).intoArray(r, i); + } + } + + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ZeroFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.zero(SPECIES).intoArray(a, i); + } + } + + assertEquals(a, r); + } + + static short[] sliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else + res[i] = (short)0; + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sliceUnaryFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.slice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector64Tests::sliceUnary); + } + + static short[] sliceBinary(short[] a, short[] b, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void sliceBinaryFloat16Vector64TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, Float16Vector64Tests::sliceBinary); + } + + static short[] slice(short[] a, short[] b, int origin, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = mask[i] ? a[idx+i+origin] : (short)0; + else { + res[i] = mask[i] ? b[idx+j] : (short)0; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void sliceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, mask, Float16Vector64Tests::slice); + } + + static short[] unsliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i < origin) + res[i] = (short)0; + else { + res[i] = a[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void unsliceUnaryFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.unslice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16Vector64Tests::unsliceUnary); + } + + static short[] unsliceBinary(short[] a, short[] b, int origin, int part, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (part == 0) { + if (i < origin) + res[i] = b[idx+i]; + else { + res[i] = a[idx+j]; + j++; + } + } else if (part == 1) { + if (i < origin) + res[i] = a[idx+SPECIES.length()-origin+i]; + else { + res[i] = b[idx+origin+j]; + j++; + } + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void unsliceBinaryFloat16Vector64TestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, Float16Vector64Tests::unsliceBinary); + } + + static short[] unslice(short[] a, short[] b, int origin, int part, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = b[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + for (int i = 0; i < SPECIES.length(); i++){ + res[i] = mask[i] ? a[idx+i] : res[i]; + } + short[] res1 = new short[SPECIES.length()]; + if (part == 0) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = b[idx+i]; + else { + res1[i] = res[j]; + j++; + } + } + } else if (part == 1) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = res[SPECIES.length()-origin+i]; + else { + res1[i] = b[idx+origin+j]; + j++; + } + } + } + return res1; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void unsliceFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, mask, Float16Vector64Tests::unslice); + } + + static short SIN(short a) { + return (short)(scalar_sin(a)); + } + + static short strictSIN(short a) { + return (short)(strict_scalar_sin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::SIN, Float16Vector64Tests::strictSIN); + } + + static short EXP(short a) { + return (short)(scalar_exp(a)); + } + + static short strictEXP(short a) { + return (short)(strict_scalar_exp(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXP).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::EXP, Float16Vector64Tests::strictEXP); + } + + static short LOG1P(short a) { + return (short)(scalar_log1p(a)); + } + + static short strictLOG1P(short a) { + return (short)(strict_scalar_log1p(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG1PFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG1P).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::LOG1P, Float16Vector64Tests::strictLOG1P); + } + + static short LOG(short a) { + return (short)(scalar_log(a)); + } + + static short strictLOG(short a) { + return (short)(strict_scalar_log(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOGFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::LOG, Float16Vector64Tests::strictLOG); + } + + static short LOG10(short a) { + return (short)(scalar_log10(a)); + } + + static short strictLOG10(short a) { + return (short)(strict_scalar_log10(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG10Float16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG10).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::LOG10, Float16Vector64Tests::strictLOG10); + } + + static short EXPM1(short a) { + return (short)(scalar_expm1(a)); + } + + static short strictEXPM1(short a) { + return (short)(strict_scalar_expm1(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPM1Float16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXPM1).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::EXPM1, Float16Vector64Tests::strictEXPM1); + } + + static short COS(short a) { + return (short)(scalar_cos(a)); + } + + static short strictCOS(short a) { + return (short)(strict_scalar_cos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::COS, Float16Vector64Tests::strictCOS); + } + + static short TAN(short a) { + return (short)(scalar_tan(a)); + } + + static short strictTAN(short a) { + return (short)(strict_scalar_tan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::TAN, Float16Vector64Tests::strictTAN); + } + + static short SINH(short a) { + return (short)(scalar_sinh(a)); + } + + static short strictSINH(short a) { + return (short)(strict_scalar_sinh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINHFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SINH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::SINH, Float16Vector64Tests::strictSINH); + } + + static short COSH(short a) { + return (short)(scalar_cosh(a)); + } + + static short strictCOSH(short a) { + return (short)(strict_scalar_cosh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSHFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COSH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::COSH, Float16Vector64Tests::strictCOSH); + } + + static short TANH(short a) { + return (short)(scalar_tanh(a)); + } + + static short strictTANH(short a) { + return (short)(strict_scalar_tanh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANHFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TANH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::TANH, Float16Vector64Tests::strictTANH); + } + + static short ASIN(short a) { + return (short)(scalar_asin(a)); + } + + static short strictASIN(short a) { + return (short)(strict_scalar_asin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ASINFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ASIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::ASIN, Float16Vector64Tests::strictASIN); + } + + static short ACOS(short a) { + return (short)(scalar_acos(a)); + } + + static short strictACOS(short a) { + return (short)(strict_scalar_acos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ACOSFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ACOS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::ACOS, Float16Vector64Tests::strictACOS); + } + + static short ATAN(short a) { + return (short)(scalar_atan(a)); + } + + static short strictATAN(short a) { + return (short)(strict_scalar_atan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ATANFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ATAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::ATAN, Float16Vector64Tests::strictATAN); + } + + static short CBRT(short a) { + return (short)(scalar_cbrt(a)); + } + + static short strictCBRT(short a) { + return (short)(strict_scalar_cbrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void CBRTFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.CBRT).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16Vector64Tests::CBRT, Float16Vector64Tests::strictCBRT); + } + + static short HYPOT(short a, short b) { + return (short)(scalar_hypot(a, b)); + } + + static short strictHYPOT(short a, short b) { + return (short)(strict_scalar_hypot(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void HYPOTFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.HYPOT, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector64Tests::HYPOT, Float16Vector64Tests::strictHYPOT); + } + + + static short POW(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictPOW(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.POW, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector64Tests::POW, Float16Vector64Tests::strictPOW); + } + + + static short pow(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictpow(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.pow(bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector64Tests::pow, Float16Vector64Tests::strictpow); + } + + + static short ATAN2(short a, short b) { + return (short)(scalar_atan2(a, b)); + } + + static short strictATAN2(short a, short b) { + return (short)(strict_scalar_atan2(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ATAN2Float16Vector64Tests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ATAN2, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16Vector64Tests::ATAN2, Float16Vector64Tests::strictATAN2); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.POW, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector64Tests::POW, Float16Vector64Tests::strictPOW); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.pow(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16Vector64Tests::pow, Float16Vector64Tests::strictpow); + } + + + static short FMA(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + static short fma(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector64Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector64Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.fma(bv, cv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, c, Float16Vector64Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector64TestsMasked(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, mask, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i]).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a, b, c, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector64TestsAltBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv).intoArray(r, i); + } + assertAltBroadcastArraysEquals(r, a, b, c, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, c, mask, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector64TestsAltBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv, vmask).intoArray(r, i); + } + + assertAltBroadcastArraysEquals(r, a, b, c, mask, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16Vector64TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector64Tests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16Vector64TestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.fma(b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16Vector64Tests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16Vector64TestsDoubleBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i], vmask).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, Float16Vector64Tests::FMA); + } + + static short NEG(short a) { + return (short)(scalar_neg((short)a)); + } + + static short neg(short a) { + return (short)(scalar_neg((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void NEGFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::NEG); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void negFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.neg().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::neg); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void NEGMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector64Tests::NEG); + } + + static short ABS(short a) { + return (short)(scalar_abs((short)a)); + } + + static short abs(short a) { + return (short)(scalar_abs((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ABSFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::ABS); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void absFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.abs().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::abs); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ABSMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector64Tests::ABS); + } + + static short SQRT(short a) { + return (short)(scalar_sqrt(a)); + } + + static short sqrt(short a) { + return (short)(scalar_sqrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SQRTFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::SQRT); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sqrtFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sqrt().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::sqrt); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void SQRTMaskedFloat16Vector64Tests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16Vector64Tests::SQRT); + } + + static boolean band(boolean a, boolean b) { + return a & b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.and(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::band); + } + + static boolean bor(boolean a, boolean b) { + return a | b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskorFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.or(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::bor); + } + + static boolean bxor(boolean a, boolean b) { + return a != b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskxorFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.xor(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::bxor); + } + + static boolean bandNot(boolean a, boolean b) { + return a & !b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandNotFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.andNot(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::bandNot); + } + + static boolean beq(boolean a, boolean b) { + return a == b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskeqFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.eq(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16Vector64Tests::beq); + } + + static boolean unot(boolean a) { + return !a; + } + + @Test(dataProvider = "boolMaskUnaryOpProvider") + static void masknotFloat16Vector64Tests(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + av.not().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16Vector64Tests::unot); + } + + private static final long LONG_MASK_BITS = 0xFFFFFFFFFFFFFFFFL >>> (64 - SPECIES.length()); + + static void assertArraysEquals(long[] r, long[] a, long bits) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], a[i] & bits); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i] & bits, "(" + a[i] + ") at index #" + i); + } + } + + @Test(dataProvider = "longMaskProvider") + static void maskFromToLongFloat16Vector64Tests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = new long[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i++) { + VectorMask vmask = VectorMask.fromLong(SPECIES, a[i]); + r[i] = vmask.toLong(); + } + } + assertArraysEquals(r, a, LONG_MASK_BITS); + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16Vector64TestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.lt(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16Vector64TestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.eq(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shorttoIntUnaryOpProvider") + static void toIntArrayFloat16Vector64TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int[] r = av.toIntArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void toLongArrayFloat16Vector64TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + long[] r = av.toLongArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toDoubleArrayFloat16Vector64TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + double[] r = av.toDoubleArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toStringFloat16Vector64TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + String str = av.toString(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void hashCodeFloat16Vector64TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int hash = av.hashCode(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + + + static long ADDReduceLong(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return (long)res; + } + + static long ADDReduceAllLong(short[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLong(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceLongFloat16Vector64Tests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEquals(r, ra, a, + Float16Vector64Tests::ADDReduceLong, Float16Vector64Tests::ADDReduceAllLong); + } + + static long ADDReduceLongMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) { + res = scalar_add(res, a[i]); + } + } + + return (long)res; + } + + static long ADDReduceAllLongMasked(short[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLongMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceLongFloat16Vector64TestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD, vmask); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEqualsMasked(r, ra, a, mask, + Float16Vector64Tests::ADDReduceLongMasked, Float16Vector64Tests::ADDReduceAllLongMasked); + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void BroadcastLongFloat16Vector64TestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, shortBitsToFloat16(a[i]).longValue()).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16Vector64TestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.blend(shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + } + assertBroadcastLongArraysEquals(r, a, b, mask, Float16Vector64Tests::blend); + } + + + @Test(dataProvider = "shortUnaryOpSelectFromProvider") + static void SelectFromFloat16Vector64Tests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat16Vector64Tests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector idxv = Float16Vector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") + static void SelectFromFloat16Vector64TestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av, vmask).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleMiscellaneousFloat16Vector64TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + int hash = shuffle.hashCode(); + int length = shuffle.length(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + assertEquals(length, SPECIES.length()); + } + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleToStringFloat16Vector64TestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + String str = shuffle.toString(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + Assert.assertTrue(str.equals("Shuffle" + Arrays.toString(subarr)), "at index " + + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + } + } + + @Test(dataProvider = "shuffleCompareOpProvider") + static void shuffleEqualsFloat16Vector64TestsSmokeTest(BiFunction fa, BiFunction fb) { + int[] a = fa.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + int[] b = fb.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = VectorShuffle.fromArray(SPECIES, a, i); + var bv = VectorShuffle.fromArray(SPECIES, b, i); + boolean eq = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(eq, Arrays.equals(a, i, to, b, i, to)); + } + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskEqualsFloat16Vector64Tests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = SPECIES.loadMask(a, i); + var bv = SPECIES.loadMask(b, i); + boolean equals = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(equals, Arrays.equals(a, i, to, b, i, to)); + } + } + } + + @Test(dataProvider = "maskProvider") + static void maskHashCodeFloat16Vector64TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + int hash = vmask.hashCode(); + + boolean subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static int maskTrueCount(boolean[] a, int idx) { + int trueCount = 0; + for (int i = idx; i < idx + SPECIES.length(); i++) { + trueCount += a[i] ? 1 : 0; + } + return trueCount; + } + + @Test(dataProvider = "maskProvider") + static void maskTrueCountFloat16Vector64TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.trueCount(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector64Tests::maskTrueCount); + } + + static int maskLastTrue(boolean[] a, int idx) { + int i = idx + SPECIES.length() - 1; + for (; i >= idx; i--) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskLastTrueFloat16Vector64TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.lastTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector64Tests::maskLastTrue); + } + + static int maskFirstTrue(boolean[] a, int idx) { + int i = idx; + for (; i < idx + SPECIES.length(); i++) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskFirstTrueFloat16Vector64TestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.firstTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16Vector64Tests::maskFirstTrue); + } + + @Test(dataProvider = "maskProvider") + static void maskCompressFloat16Vector64TestsSmokeTest(IntFunction fa) { + int trueCount = 0; + boolean[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + trueCount = vmask.trueCount(); + var rmask = vmask.compress(); + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(rmask.laneIsSet(j), j < trueCount); + } + } + } + } + + @DataProvider + public static Object[][] offsetProvider() { + return new Object[][]{ + {0}, + {-1}, + {+1}, + {+2}, + {-2}, + }; + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeFloat16Vector64TestsSmokeTest(int offset) { + int limit = SPECIES.length() * BUFFER_REPS; + for (int i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + int index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeLongFloat16Vector64TestsSmokeTest(int offset) { + long limit = SPECIES.length() * BUFFER_REPS; + for (long i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + long index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @DataProvider + public static Object[][] lengthProvider() { + return new Object[][]{ + {0}, + {1}, + {32}, + {37}, + {1024}, + {1024+1}, + {1024+5}, + }; + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundFloat16Vector64TestsSmokeTest(int length) { + int actualLoopBound = SPECIES.loopBound(length); + int expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundLongFloat16Vector64TestsSmokeTest(int _length) { + long length = _length; + long actualLoopBound = SPECIES.loopBound(length); + long expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test + static void ElementSizeFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + int elsize = av.elementSize(); + assertEquals(elsize, Float16.SIZE); + } + + @Test + static void VectorShapeFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + assert(vsh.equals(VectorShape.S_64_BIT)); + } + + @Test + static void ShapeWithLanesFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = vsh.withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void ElementTypeFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementType() == Float16.class); + } + + @Test + static void SpeciesElementSizeFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementSize() == Float16.SIZE); + } + + @Test + static void VectorTypeFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().vectorType() == av.getClass()); + } + + @Test + static void WithLanesFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorSpecies species = av.species().withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void WithShapeFloat16Vector64TestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = av.species().withShape(vsh); + assert(species.equals(SPECIES)); + } + + @Test + static void MaskAllTrueFloat16Vector64TestsSmokeTest() { + for (int ic = 0; ic < INVOC_COUNT; ic++) { + assertEquals(SPECIES.maskAll(true).toLong(), -1L >>> (64 - SPECIES.length())); + } + } +} diff --git a/test/jdk/jdk/incubator/vector/Float16VectorMaxLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float16VectorMaxLoadStoreTests.java new file mode 100644 index 00000000000..931e280c87a --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16VectorMaxLoadStoreTests.java @@ -0,0 +1,1045 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector java.base/jdk.internal.vm.annotation + * @run testng/othervm --add-opens jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED + * -XX:-TieredCompilation Float16VectorMaxLoadStoreTests + * + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.Arena; +import java.lang.foreign.ValueLayout; +import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorShape; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.internal.vm.annotation.DontInline; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.ByteOrder; +import java.util.List; +import java.util.function.*; + +@Test +public class Float16VectorMaxLoadStoreTests extends AbstractVectorLoadStoreTest { + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_MAX; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + + static VectorShape getMaxBit() { + return VectorShape.S_Max_BIT; + } + + private static final int Max = 256; // juts so we can do N/Max + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); + + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? a[i] : (short) 0, "at index #" + i); + } + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("short[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i * 5)); + }), + withToString("short[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? 1 : (short)(i + 1))); + }) + ); + + // Relative to array.length + static final List> INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl + 1", (int l) -> { + return l - SPECIES.length() + 1; + }), + withToString("l + speciesl - 1", (int l) -> { + return l + SPECIES.length() - 1; + }), + withToString("l + speciesl", (int l) -> { + return l + SPECIES.length(); + }), + withToString("l + speciesl + 1", (int l) -> { + return l + SPECIES.length() + 1; + }) + ); + + // Relative to byte[] array.length or MemorySegment.byteSize() + static final List> BYTE_INDEX_GENERATORS = List.of( + withToString("-1", (int l) -> { + return -1; + }), + withToString("l", (int l) -> { + return l; + }), + withToString("l - 1", (int l) -> { + return l - 1; + }), + withToString("l + 1", (int l) -> { + return l + 1; + }), + withToString("l - speciesl*ebsize + 1", (int l) -> { + return l - SPECIES.vectorByteSize() + 1; + }), + withToString("l + speciesl*ebsize - 1", (int l) -> { + return l + SPECIES.vectorByteSize() - 1; + }), + withToString("l + speciesl*ebsize", (int l) -> { + return l + SPECIES.vectorByteSize(); + }), + withToString("l + speciesl*ebsize + 1", (int l) -> { + return l + SPECIES.vectorByteSize() + 1; + }) + ); + + @DataProvider + public Object[][] shortProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentProvider() { + return FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, bo}; + }))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortMemorySegmentMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream(). + flatMap(fa -> MEMORY_SEGMENT_GENERATORS.stream(). + flatMap(fb -> BYTE_ORDER_VALUES.stream().map(bo -> { + return new Object[]{fa, fb, fm, bo}; + })))). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi}; + }). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortByteMaskProviderForIOOBE() { + var f = FLOAT16_GENERATORS.get(0); + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_INDEX_GENERATORS.stream().map(fi -> { + return new Object[] {f, fi, fm}; + })). + toArray(Object[][]::new); + } + + static MemorySegment toSegment(short[] a, IntFunction fb) { + MemorySegment ms = fb.apply(a.length * SPECIES.elementSize() / 8); + for (int i = 0; i < a.length; i++) { + ms.set(ELEMENT_LAYOUT, i * SPECIES.elementSize() / 8 , a[i]); + } + return ms; + } + + static short[] segmentToArray(MemorySegment ms) { + return ms.toArray(ELEMENT_LAYOUT); + } + + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + @DontInline + static VectorShuffle shuffleFromArray(int[] a, int i) { + return SPECIES.shuffleFromArray(a, i); + } + + @DontInline + static void shuffleIntoArray(VectorShuffle s, int[] a, int i) { + s.intoArray(a, i); + } + + @DontInline + static VectorShuffle shuffleFromMemorySegment(MemorySegment mem, int i, ByteOrder bo) { + return VectorShuffle.fromMemorySegment(SPECIES, mem, i, bo); + } + + @DontInline + static void shuffleIntoMemorySegment(VectorShuffle s, MemorySegment mem, int i, ByteOrder bo) { + s.intoMemorySegment(mem, i, bo); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromArray(a, i); + } + + @DontInline + static Float16Vector fromArray(short[] a, int i, VectorMask m) { + return Float16Vector.fromArray(SPECIES, a, i, m); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i) { + v.intoArray(a, i); + } + + @DontInline + static void intoArray(Float16Vector v, short[] a, int i, VectorMask m) { + v.intoArray(a, i, m); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo) { + // Tests the species method and the equivalent vector method it defers to + return (Float16Vector) SPECIES.fromMemorySegment(a, i, bo); + } + + @DontInline + static Float16Vector fromMemorySegment(MemorySegment a, int i, ByteOrder bo, VectorMask m) { + return Float16Vector.fromMemorySegment(SPECIES, a, i, bo, m); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo) { + v.intoMemorySegment(a, i, bo); + } + + @DontInline + static void intoMemorySegment(Float16Vector v, MemorySegment a, int i, ByteOrder bo, VectorMask m) { + v.intoMemorySegment(a, i, bo, m); + } + + @Test(dataProvider = "shortProvider") + static void loadStoreArray(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i); + } + } + assertEquals(r, a); + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void loadArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + fromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortProviderForIOOBE") + static void storeArrayIOOBE(IntFunction fa, IntFunction fi) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMaskArray(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, vmask); + av.intoArray(r, i); + } + } + assertArraysEquals(r, a, mask); + + + r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, vmask); + } + } + assertArraysEquals(r, a, mask); + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void loadArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = fromArray(a, i, vmask); + av.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + fromArray(a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMaskProviderForIOOBE") + static void storeArrayMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + intoArray(av, r, i, vmask); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, a.length); + try { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, 0); + intoArray(av, a, index, vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + @Test(dataProvider = "shortMaskProvider") + static void loadStoreMask(IntFunction fa, + IntFunction fm) { + boolean[] mask = fm.apply(SPECIES.length()); + boolean[] r = new boolean[mask.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < mask.length; i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, mask); + } + + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo); + } + } + long m = r.mismatch(a); + Assert.assertEquals(m, -1, "Segments not equal"); + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder()); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteProviderForIOOBE") + static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(SPECIES.vectorByteSize(), index, (int) a.byteSize()); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentMaskProvider") + static void loadStoreMemorySegmentMask(IntFunction fa, + IntFunction fb, + IntFunction fm, + ByteOrder bo) { + short[] _a = fa.apply(SPECIES.length()); + MemorySegment a = toSegment(_a, fb); + MemorySegment r = fb.apply((int) a.byteSize()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo, vmask); + av.intoMemorySegment(r, i, bo); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + + + r = fb.apply((int) a.byteSize()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, bo); + av.intoMemorySegment(r, i, bo, vmask); + } + } + assertArraysEquals(segmentToArray(r), _a, mask); + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = fromMemorySegment(a, i, ByteOrder.nativeOrder(), vmask); + av.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortByteMaskProviderForIOOBE") + static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float16.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float16.SIZE); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromValues(SPECIES, mask); + + int l = (int) a.byteSize(); + int s = SPECIES.vectorByteSize(); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + intoMemorySegment(av, r, i, ByteOrder.nativeOrder(), vmask); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8); + try { + Float16Vector av = Float16Vector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + intoMemorySegment(av, a, index, ByteOrder.nativeOrder(), vmask); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shortMemorySegmentProvider") + static void loadStoreReadonlyMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toSegment(fa.apply(SPECIES.length()), fb).asReadOnly(); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(true)) + ); + + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, SPECIES.maskAll(false)) + ); + + VectorMask m = SPECIES.shuffleFromOp(i -> i % 2 == 0 ? 1 : -1) + .laneIsValid(); + Assert.assertThrows( + UnsupportedOperationException.class, + () -> SPECIES.zero().intoMemorySegment(a, 0, bo, m) + ); + } + + + @Test(dataProvider = "maskProvider") + static void loadStoreMask(IntFunction fm) { + boolean[] a = fm.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask vmask = SPECIES.loadMask(a, i); + vmask.intoArray(r, i); + } + } + Assert.assertEquals(r, a); + } + + + @Test(dataProvider = "shuffleIntProvider") + static void loadStoreShuffleArray(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = VectorShuffle.fromArray(SPECIES, a, i); + shuffle.intoArray(r, i); + } + } + + for (int i = 0; i < a.length; i++) { + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, a[i]), r[i]); + } + + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void storeShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffleIntoArray(shuffle, r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + VectorShuffle shuffle = shuffleFromArray(a, index); + shuffleIntoArray(shuffle, r, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntProviderForIOOBE") + static void loadShuffleArrayIOOBE(IntFunction fa, IntFunction fi) { + int[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorShuffle shuffle = shuffleFromArray(a, i); + shuffle.intoArray(r, i); + } + } + + int index = fi.apply(a.length); + boolean shouldFail = isIndexOutOfBounds(SPECIES.length(), index, a.length); + try { + shuffleFromArray(a, index); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntMemorySegmentProvider") + static void loadStoreShuffleMemorySegment(IntFunction fa, + IntFunction fb, + ByteOrder bo) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), fb); + MemorySegment r = fb.apply((int) a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; //An integer for every lane is read out. So 4 bytes per lane + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = VectorShuffle.fromMemorySegment(SPECIES, a, i, bo); + shuffle.intoMemorySegment(r, i, bo); + } + } + + for (int i = 0; i < l / 4; i++) { + int ai = a.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + int ri = r.getAtIndex(ValueLayout.JAVA_INT_UNALIGNED.withOrder(bo), i); + Assert.assertEquals(testPartiallyWrapIndex(SPECIES, ai), ri); + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleLoadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = shuffleFromMemorySegment(a, i, ByteOrder.nativeOrder()); + shuffle.intoMemorySegment(r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + shuffleFromMemorySegment(a, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + @Test(dataProvider = "shuffleIntByteProviderForIOOBE") + static void shuffleStoreMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { + MemorySegment a = toShuffleSegment(SPECIES, fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize()); + + int l = (int) a.byteSize(); + int s = SPECIES.length() * 4; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < l; i += s) { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, i, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, i, ByteOrder.nativeOrder()); + } + } + + int index = fi.apply((int) a.byteSize()); + boolean shouldFail = isIndexOutOfBounds(s, index, (int) a.byteSize()); + try { + VectorShuffle shuffle = + VectorShuffle.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder()); + shuffleIntoMemorySegment(shuffle, r, index, ByteOrder.nativeOrder()); + if (shouldFail) { + Assert.fail("Failed to throw IndexOutOfBoundsException"); + } + } catch (IndexOutOfBoundsException e) { + if (!shouldFail) { + Assert.fail("Unexpected IndexOutOfBoundsException"); + } + } + } + + + + + // Gather/Scatter load/store tests + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], a[i + indexMap[j]]); + } + } + } catch (AssertionError e) { + assertEquals(r[j], a[i + indexMap[j]], "at index #" + j); + } + } + + static void assertGatherArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + int i = 0; + int j = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + j = i; + for (; j < i + SPECIES.length(); j++) { + assertEquals(r[j], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0); + } + } + } catch (AssertionError e) { + assertEquals(r[i], mask[j % SPECIES.length()] ? a[i + indexMap[j]]: (short) 0, "at index #" + j); + } + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap, boolean[] mask) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + if (mask[j % SPECIES.length()]) { + expected[i + indexMap[j]] = a[j]; + } + } + } + + assertEquals(r, expected); + } + + static void assertScatterArraysEquals(short[] r, short[] a, int[] indexMap) { + short[] expected = new short[r.length]; + + // Store before checking, since the same location may be stored to more than once + for (int i = 0; i < a.length; i += SPECIES.length()) { + for (int j = i; j < i + SPECIES.length(); j++) { + expected[i + indexMap[j]] = a[j]; + } + } + + assertEquals(r, expected); + } + + @DataProvider + public Object[][] gatherScatterProvider() { + return INT_INDEX_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] gatherScatterMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fs -> INT_INDEX_GENERATORS.stream().flatMap(fm -> + FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm, fs}; + }))). + toArray(Object[][]::new); + } + + + @Test(dataProvider = "gatherScatterProvider") + static void gather(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void gatherMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i, b, i, vmask); + av.intoArray(r, i); + } + } + + assertGatherArraysEquals(r, a, b, mask); + } + + @Test(dataProvider = "gatherScatterProvider") + static void scatter(IntFunction fa, BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i); + } + } + + assertScatterArraysEquals(r, a, b); + } + + @Test(dataProvider = "gatherScatterMaskProvider") + static void scatterMask(IntFunction fa, BiFunction fs, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] b = fs.apply(a.length, SPECIES.length()); + short[] r = new short[a.length]; + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.intoArray(r, i, b, i, vmask); + } + } + + assertScatterArraysEquals(r, a, b, mask); + } + + + +} diff --git a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java new file mode 100644 index 00000000000..92945d74ceb --- /dev/null +++ b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java @@ -0,0 +1,5904 @@ +/* + * 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 + * 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 + * @key randomness + * + * @library /test/lib + * @modules jdk.incubator.vector + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float16VectorMaxTests + */ + +// -- This file was mechanically generated: Do not edit! -- // + +import jdk.incubator.vector.VectorShape; +import jdk.incubator.vector.VectorSpecies; +import jdk.incubator.vector.VectorShuffle; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.Vector; + +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import jdk.incubator.vector.Float16Vector; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.Integer; +import java.util.List; +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Test +public class Float16VectorMaxTests extends AbstractVectorTest { + + static final VectorSpecies SPECIES = + Float16Vector.SPECIES_MAX; + + static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + static void assertEquals(short actual, short expected, short delta) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta)); + } + static void assertEquals(short actual, short expected, short delta, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta), msg); + } + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + static void assertEquals(long actual, long expected) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected)); + } + static void assertEquals(long actual, long expected, String msg) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected), msg); + } + static void assertEquals(String actual, String expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(Object actual, Object expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(double actual, double expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + static void assertEquals(boolean actual, boolean expected) { + Assert.assertEquals(actual, expected); + } + static void assertEquals(boolean actual, boolean expected, String msg) { + Assert.assertEquals(actual, expected, msg); + } + + + static VectorShape getMaxBit() { + return VectorShape.S_Max_BIT; + } + + private static final int Max = 256; // juts so we can do N/Max + + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + private static final short MIN_IDENTITY = float16ToRawShortBits(Float16.POSITIVE_INFINITY); + private static final short MUL_IDENTITY = float16ToRawShortBits(Float16.valueOf(1.0f)); + + // for floating point addition reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_ADD = float16ToRawShortBits(Float16.valueOf(10.0f)); + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final short RELATIVE_ROUNDING_ERROR_FACTOR_MUL = float16ToRawShortBits(Float16.valueOf(50.0f)); + + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); + + static void assertArraysStrictlyEquals(short[] r, short[] a) { + for (int i = 0; i < a.length; i++) { + short ir = r[i]; + short ia = a[i]; + if (ir != ia) { + Assert.fail(String.format("at index #%d, expected = %016X, actual = %016X", i, ia, ir)); + } + } + } + + interface FUnOp { + short apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "at index #" + i + ", input = " + a[i]); + } + } + + interface FUnArrayOp { + short[] apply(short a); + } + + static void assertArraysEquals(short[] r, short[] a, FUnArrayOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a[i])); + } + } catch (AssertionError e) { + short[] ref = f.apply(a[i]); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, boolean[] mask, FUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], mask[i % SPECIES.length()] ? f.apply(a[i]) : a[i], "at index #" + i + ", input = " + a[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + interface FReductionOp { + short apply(short[] a, int idx); + } + + interface FReductionAllOp { + short apply(short[] a); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (short)0.0); + } + + static void assertReductionArraysEquals(short[] r, short rc, short[] a, + FReductionOp f, FReductionAllOp fa, + short relativeErrorFactor) { + int i = 0; + try { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor)))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor)))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor))), "at index #" + i); + } + } + + interface FReductionMaskedOp { + short apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOp { + short apply(short[] a, boolean[] mask); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (short)0.0); + } + + static void assertReductionArraysEqualsMasked(short[] r, short rc, short[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + short relativeError) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError))))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError))))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError)))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError)))), "at index #" + i); + } + } + + interface FReductionOpLong { + long apply(short[] a, int idx); + } + + interface FReductionAllOpLong { + long apply(short[] a); + } + + static void assertReductionLongArraysEquals(long[] r, long rc, short[] a, + FReductionOpLong f, FReductionAllOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FReductionMaskedOpLong { + long apply(short[] a, int idx, boolean[] mask); + } + + interface FReductionAllMaskedOpLong { + long apply(short[] a, boolean[] mask); + } + + static void assertReductionLongArraysEqualsMasked(long[] r, long rc, short[] a, boolean[] mask, + FReductionMaskedOpLong f, FReductionAllMaskedOpLong fa) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask)); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask)); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + } + } + + interface FBoolReductionOp { + boolean apply(boolean[] a, int idx); + } + + static void assertReductionBoolArraysEquals(boolean[] r, boolean[] a, FBoolReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + interface FMaskReductionOp { + int apply(boolean[] a, int idx); + } + + static void assertMaskReductionArraysEquals(int[] r, boolean[] a, FMaskReductionOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a, i), "at index #" + i); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+order[i+j]]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]]); + } + } + + static void assertcompressArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + k], a[i + j]); + k++; + } + } + for (; k < vector_len; k++) { + assertEquals(r[i + k], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + k; + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[idx], a[i + j], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vector_len) { + int i = 0, j = 0, k = 0; + try { + for (; i < a.length; i += vector_len) { + k = 0; + for (j = 0; j < vector_len; j++) { + if (m[(i + j) % SPECIES.length()]) { + assertEquals(r[i + j], a[i + k]); + k++; + } else { + assertEquals(r[i + j], (short)0); + } + } + } + } catch (AssertionError e) { + int idx = i + j; + if (m[idx % SPECIES.length()]) { + assertEquals(r[idx], a[i + k], "at index #" + idx); + } else { + assertEquals(r[idx], (short)0, "at index #" + idx); + } + } + } + + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod(shortBitsToFloat16(order[idx]).intValue(), 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + } catch (AssertionError e) { + int idx = i + j; + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()]); + } + } + + static void assertRearrangeArraysEquals(short[] r, short[] a, int[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+order[i+j]], "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, boolean[] mask, int vector_len) { + int i = 0, j = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()]); + else + assertEquals(r[i+j], (short)0); + } + } + } catch (AssertionError e) { + int idx = i + j; + if (mask[j % SPECIES.length()]) + assertEquals(r[i+j], a[i+shortBitsToFloat16(order[i+j]).intValue()], "at index #" + idx + ", input = " + a[i+shortBitsToFloat16(order[i+j]).intValue()] + ", mask = " + mask[j % SPECIES.length()]); + else + assertEquals(r[i+j], (short)0, "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a) { + int i = 0; + for (; i < a.length; i += SPECIES.length()) { + int idx = i; + for (int j = idx; j < (idx + SPECIES.length()); j++) + a[j]=a[idx]; + } + + try { + for (i = 0; i < a.length; i++) { + assertEquals(r[i], a[i]); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i], "at index #" + i + ", input = " + a[i]); + } + } + + interface FBoolUnOp { + boolean apply(boolean a); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, FBoolUnOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i]), "(" + a[i] + ") at index #" + i); + } + } + + interface FBoolBinOp { + boolean apply(boolean a, boolean b); + } + + static void assertArraysEquals(boolean[] r, boolean[] a, boolean[] b, FBoolBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + interface FBinOp { + short apply(short a, short b); + } + + interface FBinMaskOp { + short apply(short a, short b, boolean m); + + static FBinMaskOp lift(FBinOp f) { + return (a, b, m) -> m ? f.apply(a, b) : a; + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i])); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i]))); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i]), c[i]), "left associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i])), "right associative test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinOp f) { + assertArraysEqualsAssociative(rl, rr, a, b, c, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEqualsAssociative(short[] rl, short[] rr, short[] a, short[] b, short[] c, boolean[] mask, FBinMaskOp f) { + int i = 0; + boolean mask_bit = false; + try { + for (; i < a.length; i++) { + mask_bit = mask[i % SPECIES.length()]; + //Left associative + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit)); + + //Right associative + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit)); + + //Results equal sanity check + assertEquals(rl[i], rr[i]); + } + } catch (AssertionError e) { + assertEquals(rl[i], f.apply(f.apply(a[i], b[i], mask_bit), c[i], mask_bit), "left associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rr[i], f.apply(a[i], f.apply(b[i], c[i], mask_bit), mask_bit), "right associative masked test at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i] + ", mask = " + mask_bit); + assertEquals(rl[i], rr[i], "Result checks not equal at index #" + i + "leftRes = " + rl[i] + ", rightRes = " + rr[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i]), "(" + a[i] + ", " + b[i] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b)); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b), "(" + a[i] + ", " + b + ") at index #" + i); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())))); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue()))), + "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinOp f) { + assertArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b, mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertBroadcastLongArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertBroadcastLongArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())), + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, FBinOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j], b[j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinOp f) { + assertShiftArraysEquals(r, a, b, mask, FBinMaskOp.lift(f)); + } + + static void assertShiftArraysEquals(short[] r, short[] a, short[] b, boolean[] mask, FBinMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], b[j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", input2 = " + b[j] + ", mask = " + mask[i]); + } + } + + interface FBinConstOp { + short apply(short a); + } + + interface FBinConstMaskOp { + short apply(short a, boolean m); + + static FBinConstMaskOp lift(FBinConstOp f) { + return (a, m) -> m ? f.apply(a) : a; + } + } + + static void assertShiftConstEquals(short[] r, short[] a, FBinConstOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j])); + } + } + } catch (AssertionError e) { + assertEquals(r[i+j], f.apply(a[i+j]), "at index #" + i + ", " + j); + } + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstOp f) { + assertShiftConstEquals(r, a, mask, FBinConstMaskOp.lift(f)); + } + + static void assertShiftConstEquals(short[] r, short[] a, boolean[] mask, FBinConstMaskOp f) { + int i = 0; + int j = 0; + try { + for (; j < a.length; j += SPECIES.length()) { + for (i = 0; i < SPECIES.length(); i++) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i])); + } + } + } catch (AssertionError err) { + assertEquals(r[i+j], f.apply(a[i+j], mask[i]), "at index #" + i + ", input1 = " + a[i+j] + ", mask = " + mask[i]); + } + } + + interface FTernOp { + short apply(short a, short b, short c); + } + + interface FTernMaskOp { + short apply(short a, short b, short c, boolean m); + + static FTernMaskOp lift(FTernOp f) { + return (a, b, c, m) -> m ? f.apply(a, b, c) : a; + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[i]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + c[i]); + } + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernOp f) { + assertArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[i], mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + c[i]); + } + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[i], c[(i / SPECIES.length()) * SPECIES.length()], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[i] + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertAltBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertAltBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], c[i], + mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[i] + ", mask = " + mask[i % SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, FTernOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()])); + } + } catch (AssertionError e) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", input3 = " + + c[(i / SPECIES.length()) * SPECIES.length()]); + } + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernOp f) { + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, FTernMaskOp.lift(f)); + } + + static void assertDoubleBroadcastArraysEquals(short[] r, short[] a, short[] b, short[] c, boolean[] mask, + FTernMaskOp f) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()])); + } + } catch (AssertionError err) { + assertEquals(r[i], f.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()], + c[(i / SPECIES.length()) * SPECIES.length()], mask[i % SPECIES.length()]), "at index #" + + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + + ", input3 = " + c[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + + mask[i % SPECIES.length()]); + } + } + + + static boolean isWithin1Ulp(short actual, short expected) { + Float16 act = shortBitsToFloat16(actual); + Float16 exp = shortBitsToFloat16(expected); + if (Float16.isNaN(exp) && !Float16.isNaN(act)) { + return false; + } else if (!Float16.isNaN(exp) && Float16.isNaN(act)) { + return false; + } + + Float16 low = Float16.nextDown(exp); + Float16 high = Float16.nextUp(exp); + + if (Float16.compare(low, exp) > 0) { + return false; + } + + if (Float16.compare(high, exp) < 0) { + return false; + } + + return true; + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, FUnOp mathf, FUnOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i]))) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i])), "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i])); + } + } + + static void assertArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0 || + isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), shortBitsToFloat16(mathf.apply(a[i], b[i]))) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); + Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i])), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], b[i])); + } + } + + static void assertBroadcastArraysEqualsWithinOneUlp(short[] r, short[] a, short[] b, + FBinOp mathf, FBinOp strictmathf) { + int i = 0; + try { + // Check that result is within 1 ulp of strict math or equivalent to math implementation. + for (; i < a.length; i++) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0 || + isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))); + } + } catch (AssertionError e) { + Assert.assertTrue(Float16.compare(shortBitsToFloat16(r[i]), + shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))) == 0, + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected = " + mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); + Assert.assertTrue(isWithin1Ulp(r[i], + strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])), + "at index #" + i + ", input1 = " + a[i] + ", input2 = " + + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], + b[(i / SPECIES.length()) * SPECIES.length()])); + } + } + + interface FGatherScatterOp { + short[] apply(short[] a, int ix, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, FGatherScatterOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + " at index #" + i); + } + } + + interface FGatherMaskedOp { + short[] apply(short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + interface FScatterMaskedOp { + short[] apply(short[] r, short[] a, int ix, boolean[] mask, int[] b, int iy); + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FGatherMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + static void assertArraysEquals(short[] r, short[] a, int[] b, boolean[] mask, FScatterMaskedOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(r, a, i, mask, b, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(r, a, i, mask, b, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, + "(ref: " + Arrays.toString(ref) + ", res: " + Arrays.toString(res) + ", a: " + + Arrays.toString(Arrays.copyOfRange(a, i, i+SPECIES.length())) + + ", b: " + + Arrays.toString(Arrays.copyOfRange(b, i, i+SPECIES.length())) + + ", r: " + + Arrays.toString(Arrays.copyOfRange(r, i, i+SPECIES.length())) + + ", mask: " + + Arrays.toString(mask) + + " at index #" + i); + } + } + + interface FLaneOp { + short[] apply(short[] a, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, int origin, FLaneOp f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i); + } + } + + interface FLaneBop { + short[] apply(short[] a, short[] b, int origin, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, FLaneBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLaneMaskedBop { + short[] apply(short[] a, short[] b, int origin, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, boolean[] mask, FLaneMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin); + } + } + + interface FLanePartBop { + short[] apply(short[] a, short[] b, int origin, int part, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, FLanePartBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + interface FLanePartMaskedBop { + short[] apply(short[] a, short[] b, int origin, int part, boolean[] mask, int idx); + } + + static void assertArraysEquals(short[] r, short[] a, short[] b, int origin, int part, boolean[] mask, FLanePartMaskedBop f) { + int i = 0; + try { + for (; i < a.length; i += SPECIES.length()) { + assertEquals(Arrays.copyOfRange(r, i, i+SPECIES.length()), + f.apply(a, b, origin, part, mask, i)); + } + } catch (AssertionError e) { + short[] ref = f.apply(a, b, origin, part, mask, i); + short[] res = Arrays.copyOfRange(r, i, i+SPECIES.length()); + assertEquals(res, ref, "(ref: " + Arrays.toString(ref) + + ", res: " + Arrays.toString(res) + + "), at index #" + i + + ", at origin #" + origin + + ", with part #" + part); + } + } + + + static short convToFloat16(int i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convIntToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504); + case 1: + return convToFloat16(-65504); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> INT_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[intCornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convIntToFloat16CornerCases(i)); + }) + ); + + static void assertArraysEquals(int[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (int)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (int)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + + static short convToFloat16(long i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static short convLongToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504L); + case 1: + return convToFloat16(-65504L); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> LONG_FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convLongToFloat16CornerCases(i)); + }) + ); + + + static void assertArraysEquals(long[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (long)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (long)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static void assertArraysEquals(double[] r, short[] a, int offs) { + int i = 0; + try { + for (; i < r.length; i++) { + assertEquals(r[i], (double)Float.float16ToFloat(a[i+offs])); + } + } catch (AssertionError e) { + assertEquals(r[i], (double)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); + } + } + + static short bits(short e) { + return e; + } + + static final List> FLOAT16_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (((short)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01f + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (i % 17 == 0) ? cornerCaseValue(i) : Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + // Create combinations of pairs + // @@@ Might be sensitive to order e.g. div by 0 + static final List>> FLOAT16_GENERATOR_PAIRS = + Stream.of(FLOAT16_GENERATORS.get(0)). + flatMap(fa -> FLOAT16_GENERATORS.stream().skip(1).map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] boolUnaryOpProvider() { + return BOOL_ARRAY_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_GENERATOR_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> FLOAT16_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float16[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT16_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT16_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortBinaryOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortIndexedOpProvider() { + return FLOAT16_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortBinaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpProvider() { + return FLOAT16_GENERATOR_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return FLOAT16_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTernaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATOR_TRIPLES.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpProvider() { + return FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoIntUnaryOpProvider() { + return INT_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shorttoLongUnaryOpProvider() { + return LONG_FLOAT16_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] maskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longMaskProvider() { + return LONG_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskBinaryOpProvider() { + return BOOLEAN_MASK_COMPARE_GENERATOR_PAIRS.stream(). + map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] boolMaskUnaryOpProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shuffleCompareOpProvider() { + return INT_SHUFFLE_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleProvider() { + return INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpShuffleMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_SHUFFLE_GENERATORS = List.of( + withToStringBi("shuffle[random]", (Integer l, Integer m) -> { + short[] a = new short[l]; + int upper = m; + for (int i = 0; i < 1; i++) { + a[i] = (short)RAND.nextInt(upper); + } + return a; + }) + ); + + @DataProvider + public Object[][] shortUnaryOpSelectFromProvider() { + return FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs}; + })). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortUnaryOpSelectFromMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_SHUFFLE_GENERATORS.stream(). + flatMap(fs -> FLOAT16_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fs, fm}; + }))). + toArray(Object[][]::new); + } + + static final List> FLOAT16_COMPARE_GENERATORS = List.of( + withToString("float16[i]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)i); + }), + withToString("float16[i - length / 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - (s * BUFFER_REPS / 2))); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i + 1)); + }), + withToString("float16[i - 2]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(i - 2)); + }), + withToString("float16[zigZag(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i%3 == 0 ? (short)i : (i%3 == 1 ? (short)(i + 1) : (short)(i - 2))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); + + static final List>> FLOAT16_TEST_GENERATOR_ARGS = + FLOAT16_COMPARE_GENERATORS.stream(). + map(fa -> List.of(fa)). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortTestOpProvider() { + return FLOAT16_TEST_GENERATOR_ARGS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortTestOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_TEST_GENERATOR_ARGS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + static final List>> FLOAT16_COMPARE_GENERATOR_PAIRS = + FLOAT16_COMPARE_GENERATORS.stream(). + flatMap(fa -> FLOAT16_COMPARE_GENERATORS.stream().map(fb -> List.of(fa, fb))). + collect(Collectors.toList()); + + @DataProvider + public Object[][] shortCompareOpProvider() { + return FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(List::toArray). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortCompareOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> FLOAT16_COMPARE_GENERATOR_PAIRS.stream().map(lfa -> { + return Stream.concat(lfa.stream(), Stream.of(fm)).toArray(); + })). + toArray(Object[][]::new); + } + + interface ToFloat16F { + short apply(int i); + } + + static short[] fill(int s , ToFloat16F f) { + return fill(new short[s], f); + } + + static short[] fill(short[] a, ToFloat16F f) { + for (int i = 0; i < a.length; i++) { + a[i] = f.apply(i); + } + return a; + } + + static short cornerCaseValue(int i) { + return switch(i % 10) { + case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); + case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); + case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); + case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); + case 4 -> float16ToRawShortBits(Float16.NaN); + case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); + default -> float16ToShortBits(Float16.valueOf(-0.0f)); + }; + } + + static final IntFunction fr = (vl) -> { + int length = BUFFER_REPS * vl; + return new short[length]; + }; + + static final IntFunction fmr = (vl) -> { + int length = BUFFER_REPS * vl; + return new boolean[length]; + }; + + static final IntFunction lfr = (vl) -> { + int length = BUFFER_REPS * vl; + return new long[length]; + }; + + static boolean eq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() == bt.floatValue(); + } + + static boolean neq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() != bt.floatValue(); + } + + static boolean lt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() < bt.floatValue(); + } + + static boolean le(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() <= bt.floatValue(); + } + + static boolean gt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() > bt.floatValue(); + } + + static boolean ge(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() >= bt.floatValue(); + } + + static short firstNonZero(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 zero = shortBitsToFloat16((short)0); + return Float16.compare(at, zero) != 0 ? a : b; + } + + static short scalar_add(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.add(at, bt)); + } + + static short scalar_sub(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.subtract(at, bt)); + } + + static short scalar_mul(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.multiply(at, bt)); + + } + static short scalar_max(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.max(at, bt)); + } + + static short scalar_min(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.min(at, bt)); + } + + static short scalar_div(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.divide(at, bt)); + } + + static short scalar_fma(short a, short b, short c) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 ct = shortBitsToFloat16(c); + return float16ToRawShortBits(Float16.fma(at, bt, ct)); + } + + static short scalar_abs(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.abs(at)); + } + + static short scalar_neg(short a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.negate(at)); + } + + static short scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(Math.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(Math.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_sin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_exp(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log1p(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_log10(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_expm1(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sinh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cosh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_tanh(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_asin(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_acos(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_atan(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_cbrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_sqrt(short a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static short strict_scalar_hypot(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_pow(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static short strict_scalar_atan2(short a, short b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static boolean isNaN(short a) { + return Float16.isNaN(shortBitsToFloat16(a)); + } + static boolean isFinite(short a) { + return Float16.isFinite(shortBitsToFloat16(a)); + } + static boolean isInfinite(short a) { + return Float16.isInfinite(shortBitsToFloat16(a)); + } + + @Test + static void smokeTest1() { + Float16Vector three = Float16Vector.broadcast(SPECIES, float16ToRawShortBits(Float16.valueOf(-3))); + Float16Vector three2 = (Float16Vector) SPECIES.broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three2).allTrue()); + Float16Vector three3 = three2.broadcast(float16ToRawShortBits(Float16.valueOf(1))).broadcast(Float16.valueOf(-3).longValue()); + assert(three.eq(three3).allTrue()); + int scale = 2; + Class ETYPE = short.class; + if (ETYPE == double.class || ETYPE == long.class) + scale = 1000000; + else if (ETYPE == byte.class && SPECIES.length() >= 64) + scale = 1; + Float16Vector higher = three.addIndex(scale); + VectorMask m = three.compare(VectorOperators.LE, higher); + assert(m.allTrue()); + m = higher.min(float16ToRawShortBits(Float16.valueOf(-1))).test(VectorOperators.IS_NEGATIVE); + assert(m.allTrue()); + m = higher.test(VectorOperators.IS_FINITE); + assert(m.allTrue()); + short max = higher.reduceLanes(VectorOperators.MAX); + assert(max == float16ToRawShortBits(Float16.add(Float16.valueOf(-3), Float16.multiply(Float16.valueOf(scale), Float16.valueOf((SPECIES.length()-1)))))); + } + + private static short[] + bothToArray(Float16Vector a, Float16Vector b) { + short[] r = new short[a.length() + b.length()]; + a.intoArray(r, 0); + b.intoArray(r, a.length()); + return r; + } + + @Test + static void smokeTest2() { + // Do some zipping and shuffling. + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES,0,1,false).toVector(); + assertEquals(io, io2); + Float16Vector a = io.add((short)1); //[1,2] + Float16Vector b = a.neg(); //[-1,-2] + short[] abValues = bothToArray(a,b); //[1,2,-1,-2] + VectorShuffle zip0 = VectorShuffle.makeZip(SPECIES, 0); + VectorShuffle zip1 = VectorShuffle.makeZip(SPECIES, 1); + Float16Vector zab0 = a.rearrange(zip0,b); //[1,-1] + Float16Vector zab1 = a.rearrange(zip1,b); //[2,-2] + short[] zabValues = bothToArray(zab0, zab1); //[1,-1,2,-2] + // manually zip + short[] manual = new short[zabValues.length]; + for (int i = 0; i < manual.length; i += 2) { + manual[i+0] = abValues[i/2]; + manual[i+1] = abValues[a.length() + i/2]; + } + assertEquals(Arrays.toString(zabValues), Arrays.toString(manual)); + VectorShuffle unz0 = VectorShuffle.makeUnzip(SPECIES, 0); + VectorShuffle unz1 = VectorShuffle.makeUnzip(SPECIES, 1); + Float16Vector uab0 = zab0.rearrange(unz0,zab1); + Float16Vector uab1 = zab0.rearrange(unz1,zab1); + short[] abValues1 = bothToArray(uab0, uab1); + assertEquals(Arrays.toString(abValues), Arrays.toString(abValues1)); + } + + static void iotaShuffle() { + Float16Vector io = (Float16Vector) SPECIES.broadcast(0).addIndex(1); + Float16Vector io2 = (Float16Vector) VectorShuffle.iota(SPECIES, 0 , 1, false).toVector(); + assertEquals(io, io2); + } + + @Test + // Test all shuffle related operations. + static void shuffleTest() { + // To test backend instructions, make sure that C2 is used. + for (int loop = 0; loop < INVOC_COUNT * INVOC_COUNT; loop++) { + iotaShuffle(); + } + } + + @Test + void viewAsIntegeralLanesTest() { + Vector asIntegral = SPECIES.zero().viewAsIntegralLanes(); + VectorSpecies asIntegralSpecies = asIntegral.species(); + Assert.assertNotEquals(asIntegralSpecies.elementType(), SPECIES.elementType()); + assertEquals(asIntegralSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asIntegralSpecies.length(), SPECIES.length()); + assertEquals(asIntegral.viewAsFloatingLanes().species(), SPECIES); + } + + @Test + void viewAsFloatingLanesTest() { + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + assertEquals(asFloating.species(), SPECIES); + } + + static short ADD(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::ADD); + } + + static short add(short a, short b) { + return (short)(scalar_add(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ADD, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.add(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::add); + } + + static short SUB(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void SUBFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::SUB); + } + + static short sub(short a, short b) { + return (short)(scalar_sub(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void SUBFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.SUB, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::SUB); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.sub(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::sub); + } + + static short MUL(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MULFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::MUL); + } + + static short mul(short a, short b) { + return (short)(scalar_mul(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void MULFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MUL, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::MUL); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.mul(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::mul); + } + + static short DIV(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void DIVFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::DIV); + } + + static short div(short a, short b) { + return (short)(scalar_div(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void DIVFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.DIV, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::DIV); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.div(bv, vmask).intoArray(r, i); + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::div); + } + + static short FIRST_NONZERO(short a, short b) { + return (short)(firstNonZero(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void FIRST_NONZEROFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void FIRST_NONZEROFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FIRST_NONZERO, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::FIRST_NONZERO); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void addFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::add); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void addFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.add(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16VectorMaxTests::add); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void subFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::sub); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void subFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sub(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16VectorMaxTests::sub); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void mulFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::mul); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void mulFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.mul(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16VectorMaxTests::mul); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void divFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::div); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void divFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.div(b[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, mask, Float16VectorMaxTests::div); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ADDFloat16VectorMaxTestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue()).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, Float16VectorMaxTests::ADD); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void ADDFloat16VectorMaxTestsBroadcastMaskedLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ADD, shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + + assertBroadcastLongArraysEquals(r, a, b, mask, Float16VectorMaxTests::ADD); + } + + static Float16Vector bv_MIN = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINFloat16VectorMaxTestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16VectorMaxTests::MIN); + } + + static Float16Vector bv_min = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void minFloat16VectorMaxTestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(bv_min).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16VectorMaxTests::min); + } + + static Float16Vector bv_MIN_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINFloat16VectorMaxTestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, bv_MIN_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16VectorMaxTests::MIN); + } + + static Float16Vector bv_MAX = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXFloat16VectorMaxTestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16VectorMaxTests::MAX); + } + + static Float16Vector bv_max = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpProvider") + static void maxFloat16VectorMaxTestsWithMemOp(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(bv_max).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, Float16VectorMaxTests::max); + } + + static Float16Vector bv_MAX_M = Float16Vector.broadcast(SPECIES, (short)10); + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXFloat16VectorMaxTestsMaskedWithMemOp(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, bv_MAX_M, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, (short)10, mask, Float16VectorMaxTests::MAX); + } + + static short MIN(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MIN, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::MIN); + } + + static short min(short a, short b) { + return (short)(scalar_min(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.min(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::min); + } + + static short MAX(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.MAX, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::MAX); + } + + static short max(short a, short b) { + return (short)(scalar_max(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.max(bv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::max); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MINFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MIN, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::MIN); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void minFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.min(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::min); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void MAXFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.MAX, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::MAX); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void maxFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.max(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, Float16VectorMaxTests::max); + } + + static short ADDReduce(short[] a, int idx) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAll(short[] a) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16VectorMaxTests::ADDReduce, Float16VectorMaxTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + assertEquals((short) (scalar_add(id, id)), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_add(id, x)), x); + assertEquals((short) (scalar_add(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_add(id, x)), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_add(x, id)), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_add(res, a[i]); + } + + return res; + } + + static short ADDReduceAllMasked(short[] a, boolean[] mask) { + short res = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_add(res, ADDReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra = scalar_add(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16VectorMaxTests::ADDReduceMasked, Float16VectorMaxTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); + } + + static short MULReduce(short[] a, int idx) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAll(short[] a) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16VectorMaxTests::MULReduce, Float16VectorMaxTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + assertEquals((short) (scalar_mul(id, id)), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals((short) (scalar_mul(id, x)), x); + assertEquals((short) (scalar_mul(x, id)), x); + } + } catch (AssertionError e) { + assertEquals((short) (scalar_mul(id, x)), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + assertEquals((short) (scalar_mul(x, id)), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_mul(res, a[i]); + } + + return res; + } + + static short MULReduceAllMasked(short[] a, boolean[] mask) { + short res = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_mul(res, MULReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MULReduceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra = scalar_mul(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16VectorMaxTests::MULReduceMasked, Float16VectorMaxTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); + } + + static short MINReduce(short[] a, int idx) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAll(short[] a) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16VectorMaxTests::MINReduce, Float16VectorMaxTests::MINReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + assertEquals(scalar_min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_min(id, x), x); + assertEquals(scalar_min(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_min(res, a[i]); + } + + return res; + } + + static short MINReduceAllMasked(short[] a, boolean[] mask) { + short res = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_min(res, MINReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MINReduceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = scalar_min(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16VectorMaxTests::MINReduceMasked, Float16VectorMaxTests::MINReduceAllMasked); + } + + static short MAXReduce(short[] a, int idx) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAll(short[] a) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16VectorMaxTests::MAXReduce, Float16VectorMaxTests::MAXReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + assertEquals(scalar_max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(scalar_max(id, x), x); + assertEquals(scalar_max(x, id), x); + } + } catch (AssertionError e) { + assertEquals(scalar_max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + assertEquals(scalar_max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = scalar_max(res, a[i]); + } + + return res; + } + + static short MAXReduceAllMasked(short[] a, boolean[] mask) { + short res = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = scalar_max(res, MAXReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void MAXReduceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = scalar_max(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16VectorMaxTests::MAXReduceMasked, Float16VectorMaxTests::MAXReduceAllMasked); + } + + static short FIRST_NONZEROReduce(short[] a, int idx) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAll(short[] a) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEquals(r, ra, a, + Float16VectorMaxTests::FIRST_NONZEROReduce, Float16VectorMaxTests::FIRST_NONZEROReduceAll); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + assertEquals(firstNonZero(id, x), x); + assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = firstNonZero(res, a[i]); + } + + return res; + } + + static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { + short res = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void FIRST_NONZEROReduceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Float16VectorMaxTests::FIRST_NONZEROReduceMasked, Float16VectorMaxTests::FIRST_NONZEROReduceAllMasked); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void withFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0, j = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.withLane(j, b[i + j]).intoArray(r, i); + a[i + j] = b[i + j]; + j = (j + 1) & (SPECIES.length() - 1); + } + } + + + assertArraysStrictlyEquals(r, a); + } + + static boolean testIS_DEFAULT(short a) { + return bits(a)==0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_DEFAULTFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_DEFAULT(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_DEFAULTMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_DEFAULT, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_DEFAULT(a[i + j])); + } + } + } + } + + static boolean testIS_NEGATIVE(short a) { + return bits(a)<0; + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NEGATIVEFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NEGATIVE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NEGATIVEMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NEGATIVE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NEGATIVE(a[i + j])); + } + } + } + } + + static boolean testIS_FINITE(short a) { + return isFinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_FINITEFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_FINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_FINITEMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_FINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_FINITE(a[i + j])); + } + } + } + } + + static boolean testIS_NAN(short a) { + return isNaN(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_NANFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_NAN(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_NANMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_NAN, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_NAN(a[i + j])); + } + } + } + } + + static boolean testIS_INFINITE(short a) { + return isInfinite(a); + } + + @Test(dataProvider = "shortTestOpProvider") + static void IS_INFINITEFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortTestOpMaskProvider") + static void IS_INFINITEMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.test(VectorOperators.IS_INFINITE, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), vmask.laneIsSet(j) && testIS_INFINITE(a[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.lt(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && lt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GTFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GTFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GT, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && gt(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.eq(bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.EQ, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && eq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void NEFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void NEFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.NE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && neq(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LEFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LEFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.LE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && le(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void GEFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void GEFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + VectorMask mv = av.compare(VectorOperators.GE, bv, vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && ge(a[i + j], b[i + j])); + } + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void LTFloat16VectorMaxTestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void LTFloat16VectorMaxTestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.LT, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (lt(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, b[i], vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], b[i]))); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void EQFloat16VectorMaxTestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue()); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())))); + } + } + } + + @Test(dataProvider = "shortCompareOpMaskProvider") + static void EQFloat16VectorMaxTestsBroadcastLongMaskedSmokeTest(IntFunction fa, + IntFunction fb, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.compare(VectorOperators.EQ, shortBitsToFloat16(b[i]).longValue(), vmask); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), mask[j] && (eq(a[i + j], float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue()))))); + } + } + } + + static short blend(short a, short b, boolean mask) { + return mask ? b : a; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16VectorMaxTests(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.blend(bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, mask, Float16VectorMaxTests::blend); + } + + @Test(dataProvider = "shortUnaryOpShuffleProvider") + static void RearrangeFloat16VectorMaxTests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i)).intoArray(r, i); + } + } + + assertRearrangeArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpShuffleMaskProvider") + static void RearrangeFloat16VectorMaxTestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + int[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.rearrange(VectorShuffle.fromArray(SPECIES, order, i), vmask).intoArray(r, i); + } + + assertRearrangeArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void compressFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.compress(vmask).intoArray(r, i); + } + } + + assertcompressArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void expandFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.expand(vmask).intoArray(r, i); + } + } + + assertexpandArraysEquals(r, a, mask, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void getFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int num_lanes = SPECIES.length(); + // Manually unroll because full unroll happens after intrinsification. + // Unroll is needed because get intrinsic requires for index to be a known constant. + if (num_lanes == 1) { + r[i]=av.lane(0); + } else if (num_lanes == 2) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + } else if (num_lanes == 4) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + } else if (num_lanes == 8) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + } else if (num_lanes == 16) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + } else if (num_lanes == 32) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + } else if (num_lanes == 64) { + r[i]=av.lane(0); + r[i+1]=av.lane(1); + r[i+2]=av.lane(2); + r[i+3]=av.lane(3); + r[i+4]=av.lane(4); + r[i+5]=av.lane(5); + r[i+6]=av.lane(6); + r[i+7]=av.lane(7); + r[i+8]=av.lane(8); + r[i+9]=av.lane(9); + r[i+10]=av.lane(10); + r[i+11]=av.lane(11); + r[i+12]=av.lane(12); + r[i+13]=av.lane(13); + r[i+14]=av.lane(14); + r[i+15]=av.lane(15); + r[i+16]=av.lane(16); + r[i+17]=av.lane(17); + r[i+18]=av.lane(18); + r[i+19]=av.lane(19); + r[i+20]=av.lane(20); + r[i+21]=av.lane(21); + r[i+22]=av.lane(22); + r[i+23]=av.lane(23); + r[i+24]=av.lane(24); + r[i+25]=av.lane(25); + r[i+26]=av.lane(26); + r[i+27]=av.lane(27); + r[i+28]=av.lane(28); + r[i+29]=av.lane(29); + r[i+30]=av.lane(30); + r[i+31]=av.lane(31); + r[i+32]=av.lane(32); + r[i+33]=av.lane(33); + r[i+34]=av.lane(34); + r[i+35]=av.lane(35); + r[i+36]=av.lane(36); + r[i+37]=av.lane(37); + r[i+38]=av.lane(38); + r[i+39]=av.lane(39); + r[i+40]=av.lane(40); + r[i+41]=av.lane(41); + r[i+42]=av.lane(42); + r[i+43]=av.lane(43); + r[i+44]=av.lane(44); + r[i+45]=av.lane(45); + r[i+46]=av.lane(46); + r[i+47]=av.lane(47); + r[i+48]=av.lane(48); + r[i+49]=av.lane(49); + r[i+50]=av.lane(50); + r[i+51]=av.lane(51); + r[i+52]=av.lane(52); + r[i+53]=av.lane(53); + r[i+54]=av.lane(54); + r[i+55]=av.lane(55); + r[i+56]=av.lane(56); + r[i+57]=av.lane(57); + r[i+58]=av.lane(58); + r[i+59]=av.lane(59); + r[i+60]=av.lane(60); + r[i+61]=av.lane(61); + r[i+62]=av.lane(62); + r[i+63]=av.lane(63); + } else { + for (int j = 0; j < SPECIES.length(); j++) { + r[i+j]=av.lane(j); + } + } + } + } + + assertArraysStrictlyEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void BroadcastFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, a[i]).intoArray(r, i); + } + } + + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ZeroFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.zero(SPECIES).intoArray(a, i); + } + } + + assertEquals(a, r); + } + + static short[] sliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else + res[i] = (short)0; + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sliceUnaryFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.slice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16VectorMaxTests::sliceUnary); + } + + static short[] sliceBinary(short[] a, short[] b, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = a[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void sliceBinaryFloat16VectorMaxTestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, Float16VectorMaxTests::sliceBinary); + } + + static short[] slice(short[] a, short[] b, int origin, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = mask[i] ? a[idx+i+origin] : (short)0; + else { + res[i] = mask[i] ? b[idx+j] : (short)0; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void sliceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.slice(origin, bv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, mask, Float16VectorMaxTests::slice); + } + + static short[] unsliceUnary(short[] a, int origin, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i < origin) + res[i] = (short)0; + else { + res[i] = a[idx+j]; + j++; + } + } + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void unsliceUnaryFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.unslice(origin).intoArray(r, i); + } + } + + assertArraysEquals(r, a, origin, Float16VectorMaxTests::unsliceUnary); + } + + static short[] unsliceBinary(short[] a, short[] b, int origin, int part, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (part == 0) { + if (i < origin) + res[i] = b[idx+i]; + else { + res[i] = a[idx+j]; + j++; + } + } else if (part == 1) { + if (i < origin) + res[i] = a[idx+SPECIES.length()-origin+i]; + else { + res[i] = b[idx+origin+j]; + j++; + } + } + } + return res; + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void unsliceBinaryFloat16VectorMaxTestsBinary(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, Float16VectorMaxTests::unsliceBinary); + } + + static short[] unslice(short[] a, short[] b, int origin, int part, boolean[] mask, int idx) { + short[] res = new short[SPECIES.length()]; + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if(i+origin < SPECIES.length()) + res[i] = b[idx+i+origin]; + else { + res[i] = b[idx+j]; + j++; + } + } + for (int i = 0; i < SPECIES.length(); i++){ + res[i] = mask[i] ? a[idx+i] : res[i]; + } + short[] res1 = new short[SPECIES.length()]; + if (part == 0) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = b[idx+i]; + else { + res1[i] = res[j]; + j++; + } + } + } else if (part == 1) { + for (int i = 0, j = 0; i < SPECIES.length(); i++){ + if (i < origin) + res1[i] = res[SPECIES.length()-origin+i]; + else { + res1[i] = b[idx+origin+j]; + j++; + } + } + } + return res1; + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void unsliceFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short[] r = new short[a.length]; + int origin = RAND.nextInt(SPECIES.length()); + int part = RAND.nextInt(2); + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.unslice(origin, bv, part, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, origin, part, mask, Float16VectorMaxTests::unslice); + } + + static short SIN(short a) { + return (short)(scalar_sin(a)); + } + + static short strictSIN(short a) { + return (short)(strict_scalar_sin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::SIN, Float16VectorMaxTests::strictSIN); + } + + static short EXP(short a) { + return (short)(scalar_exp(a)); + } + + static short strictEXP(short a) { + return (short)(strict_scalar_exp(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXP).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::EXP, Float16VectorMaxTests::strictEXP); + } + + static short LOG1P(short a) { + return (short)(scalar_log1p(a)); + } + + static short strictLOG1P(short a) { + return (short)(strict_scalar_log1p(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG1PFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG1P).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::LOG1P, Float16VectorMaxTests::strictLOG1P); + } + + static short LOG(short a) { + return (short)(scalar_log(a)); + } + + static short strictLOG(short a) { + return (short)(strict_scalar_log(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOGFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::LOG, Float16VectorMaxTests::strictLOG); + } + + static short LOG10(short a) { + return (short)(scalar_log10(a)); + } + + static short strictLOG10(short a) { + return (short)(strict_scalar_log10(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void LOG10Float16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.LOG10).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::LOG10, Float16VectorMaxTests::strictLOG10); + } + + static short EXPM1(short a) { + return (short)(scalar_expm1(a)); + } + + static short strictEXPM1(short a) { + return (short)(strict_scalar_expm1(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void EXPM1Float16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.EXPM1).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::EXPM1, Float16VectorMaxTests::strictEXPM1); + } + + static short COS(short a) { + return (short)(scalar_cos(a)); + } + + static short strictCOS(short a) { + return (short)(strict_scalar_cos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::COS, Float16VectorMaxTests::strictCOS); + } + + static short TAN(short a) { + return (short)(scalar_tan(a)); + } + + static short strictTAN(short a) { + return (short)(strict_scalar_tan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::TAN, Float16VectorMaxTests::strictTAN); + } + + static short SINH(short a) { + return (short)(scalar_sinh(a)); + } + + static short strictSINH(short a) { + return (short)(strict_scalar_sinh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SINHFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SINH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::SINH, Float16VectorMaxTests::strictSINH); + } + + static short COSH(short a) { + return (short)(scalar_cosh(a)); + } + + static short strictCOSH(short a) { + return (short)(strict_scalar_cosh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void COSHFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.COSH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::COSH, Float16VectorMaxTests::strictCOSH); + } + + static short TANH(short a) { + return (short)(scalar_tanh(a)); + } + + static short strictTANH(short a) { + return (short)(strict_scalar_tanh(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void TANHFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.TANH).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::TANH, Float16VectorMaxTests::strictTANH); + } + + static short ASIN(short a) { + return (short)(scalar_asin(a)); + } + + static short strictASIN(short a) { + return (short)(strict_scalar_asin(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ASINFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ASIN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::ASIN, Float16VectorMaxTests::strictASIN); + } + + static short ACOS(short a) { + return (short)(scalar_acos(a)); + } + + static short strictACOS(short a) { + return (short)(strict_scalar_acos(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ACOSFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ACOS).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::ACOS, Float16VectorMaxTests::strictACOS); + } + + static short ATAN(short a) { + return (short)(scalar_atan(a)); + } + + static short strictATAN(short a) { + return (short)(strict_scalar_atan(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ATANFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ATAN).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::ATAN, Float16VectorMaxTests::strictATAN); + } + + static short CBRT(short a) { + return (short)(scalar_cbrt(a)); + } + + static short strictCBRT(short a) { + return (short)(strict_scalar_cbrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void CBRTFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.CBRT).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, Float16VectorMaxTests::CBRT, Float16VectorMaxTests::strictCBRT); + } + + static short HYPOT(short a, short b) { + return (short)(scalar_hypot(a, b)); + } + + static short strictHYPOT(short a, short b) { + return (short)(strict_scalar_hypot(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void HYPOTFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.HYPOT, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16VectorMaxTests::HYPOT, Float16VectorMaxTests::strictHYPOT); + } + + + static short POW(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictPOW(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.POW, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16VectorMaxTests::POW, Float16VectorMaxTests::strictPOW); + } + + + static short pow(short a, short b) { + return (short)(scalar_pow(a, b)); + } + + static short strictpow(short a, short b) { + return (short)(strict_scalar_pow(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.pow(bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16VectorMaxTests::pow, Float16VectorMaxTests::strictpow); + } + + + static short ATAN2(short a, short b) { + return (short)(scalar_atan2(a, b)); + } + + static short strictATAN2(short a, short b) { + return (short)(strict_scalar_atan2(a, b)); + } + + @Test(dataProvider = "shortBinaryOpProvider") + static void ATAN2Float16VectorMaxTests(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.ATAN2, bv).intoArray(r, i); + } + } + + assertArraysEqualsWithinOneUlp(r, a, b, Float16VectorMaxTests::ATAN2, Float16VectorMaxTests::strictATAN2); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void POWFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.POW, b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16VectorMaxTests::POW, Float16VectorMaxTests::strictPOW); + } + + + @Test(dataProvider = "shortBinaryOpProvider") + static void powFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.pow(b[i]).intoArray(r, i); + } + + assertBroadcastArraysEqualsWithinOneUlp(r, a, b, Float16VectorMaxTests::pow, Float16VectorMaxTests::strictpow); + } + + + static short FMA(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + static short fma(short a, short b, short c) { + return (short)(scalar_fma(a, b, c)); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16VectorMaxTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16VectorMaxTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.fma(bv, cv).intoArray(r, i); + } + + assertArraysEquals(r, a, b, c, Float16VectorMaxTests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, bv, cv, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, c, mask, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i]).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a, b, c, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16VectorMaxTestsAltBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv).intoArray(r, i); + } + assertAltBroadcastArraysEquals(r, a, b, c, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + av.lanewise(VectorOperators.FMA, bv, c[i], vmask).intoArray(r, i); + } + + assertBroadcastArraysEquals(r, a, b, c, mask, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16VectorMaxTestsAltBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector cv = Float16Vector.fromArray(SPECIES, c, i); + av.lanewise(VectorOperators.FMA, b[i], cv, vmask).intoArray(r, i); + } + + assertAltBroadcastArraysEquals(r, a, b, c, mask, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void FMAFloat16VectorMaxTestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16VectorMaxTests::FMA); + } + + @Test(dataProvider = "shortTernaryOpProvider") + static void fmaFloat16VectorMaxTestsDoubleBroadcastSmokeTest(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.fma(b[i], c[i]).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, Float16VectorMaxTests::fma); + } + + @Test(dataProvider = "shortTernaryOpMaskProvider") + static void FMAFloat16VectorMaxTestsDoubleBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fc, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] c = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.FMA, b[i], c[i], vmask).intoArray(r, i); + } + + assertDoubleBroadcastArraysEquals(r, a, b, c, mask, Float16VectorMaxTests::FMA); + } + + static short NEG(short a) { + return (short)(scalar_neg((short)a)); + } + + static short neg(short a) { + return (short)(scalar_neg((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void NEGFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::NEG); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void negFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.neg().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::neg); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void NEGMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.NEG, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16VectorMaxTests::NEG); + } + + static short ABS(short a) { + return (short)(scalar_abs((short)a)); + } + + static short abs(short a) { + return (short)(scalar_abs((short)a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ABSFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::ABS); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void absFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.abs().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::abs); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ABSMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.ABS, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16VectorMaxTests::ABS); + } + + static short SQRT(short a) { + return (short)(scalar_sqrt(a)); + } + + static short sqrt(short a) { + return (short)(scalar_sqrt(a)); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void SQRTFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT).intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::SQRT); + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void sqrtFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.sqrt().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::sqrt); + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void SQRTMaskedFloat16VectorMaxTests(IntFunction fa, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.lanewise(VectorOperators.SQRT, vmask).intoArray(r, i); + } + } + + assertArraysEquals(r, a, mask, Float16VectorMaxTests::SQRT); + } + + static boolean band(boolean a, boolean b) { + return a & b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.and(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::band); + } + + static boolean bor(boolean a, boolean b) { + return a | b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskorFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.or(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::bor); + } + + static boolean bxor(boolean a, boolean b) { + return a != b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskxorFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.xor(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::bxor); + } + + static boolean bandNot(boolean a, boolean b) { + return a & !b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskandNotFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.andNot(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::bandNot); + } + + static boolean beq(boolean a, boolean b) { + return a == b; + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskeqFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + VectorMask bv = SPECIES.loadMask(b, i); + av.eq(bv).intoArray(r, i); + } + } + + assertArraysEquals(r, a, b, Float16VectorMaxTests::beq); + } + + static boolean unot(boolean a) { + return !a; + } + + @Test(dataProvider = "boolMaskUnaryOpProvider") + static void masknotFloat16VectorMaxTests(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] r = new boolean[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + VectorMask av = SPECIES.loadMask(a, i); + av.not().intoArray(r, i); + } + } + + assertArraysEquals(r, a, Float16VectorMaxTests::unot); + } + + private static final long LONG_MASK_BITS = 0xFFFFFFFFFFFFFFFFL >>> (64 - SPECIES.length()); + + static void assertArraysEquals(long[] r, long[] a, long bits) { + int i = 0; + try { + for (; i < a.length; i++) { + assertEquals(r[i], a[i] & bits); + } + } catch (AssertionError e) { + assertEquals(r[i], a[i] & bits, "(" + a[i] + ") at index #" + i); + } + } + + @Test(dataProvider = "longMaskProvider") + static void maskFromToLongFloat16VectorMaxTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = new long[a.length]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i++) { + VectorMask vmask = VectorMask.fromLong(SPECIES, a[i]); + r[i] = vmask.toLong(); + } + } + assertArraysEquals(r, a, LONG_MASK_BITS); + } + + @Test(dataProvider = "shortCompareOpProvider") + static void ltFloat16VectorMaxTestsBroadcastSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.lt(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), lt(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shortCompareOpProvider") + static void eqFloat16VectorMaxTestsBroadcastMaskedSmokeTest(IntFunction fa, IntFunction fb) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + VectorMask mv = av.eq(b[i]); + + // Check results as part of computation. + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(mv.laneIsSet(j), eq(a[i + j], b[i])); + } + } + } + + @Test(dataProvider = "shorttoIntUnaryOpProvider") + static void toIntArrayFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int[] r = av.toIntArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void toLongArrayFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + long[] r = av.toLongArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toDoubleArrayFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + double[] r = av.toDoubleArray(); + assertArraysEquals(r, a, i); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void toStringFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + String str = av.toString(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); + } + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void hashCodeFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + int hash = av.hashCode(); + + short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + + + static long ADDReduceLong(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = scalar_add(res, a[i]); + } + + return (long)res; + } + + static long ADDReduceAllLong(short[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLong(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceLongFloat16VectorMaxTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEquals(r, ra, a, + Float16VectorMaxTests::ADDReduceLong, Float16VectorMaxTests::ADDReduceAllLong); + } + + static long ADDReduceLongMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) { + res = scalar_add(res, a[i]); + } + } + + return (long)res; + } + + static long ADDReduceAllLongMasked(short[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long)scalar_add((short)res, (short)ADDReduceLongMasked(a, i, mask)); + } + + return res; + } + + @Test(dataProvider = "shortUnaryOpMaskProvider") + static void ADDReduceLongFloat16VectorMaxTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + long[] r = lfr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanesToLong(VectorOperators.ADD, vmask); + } + + ra = 0; + for (int i = 0; i < a.length; i++) { + ra = (long)scalar_add((short)ra, (short)r[i]); + } + + assertReductionLongArraysEqualsMasked(r, ra, a, mask, + Float16VectorMaxTests::ADDReduceLongMasked, Float16VectorMaxTests::ADDReduceAllLongMasked); + } + + @Test(dataProvider = "shorttoLongUnaryOpProvider") + static void BroadcastLongFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = new short[a.length]; + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector.broadcast(SPECIES, shortBitsToFloat16(a[i]).longValue()).intoArray(r, i); + } + assertBroadcastArraysEquals(r, a); + } + + @Test(dataProvider = "shortBinaryOpMaskProvider") + static void blendFloat16VectorMaxTestsBroadcastLongSmokeTest(IntFunction fa, IntFunction fb, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + av.blend(shortBitsToFloat16(b[i]).longValue(), vmask).intoArray(r, i); + } + } + assertBroadcastLongArraysEquals(r, a, b, mask, Float16VectorMaxTests::blend); + } + + + @Test(dataProvider = "shortUnaryOpSelectFromProvider") + static void SelectFromFloat16VectorMaxTests(IntFunction fa, + BiFunction fs) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, SPECIES.length()); + } + + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat16VectorMaxTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, b, i); + Float16Vector idxv = Float16Vector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") + static void SelectFromFloat16VectorMaxTestsMaskedSmokeTest(IntFunction fa, + BiFunction fs, + IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] order = fs.apply(a.length, SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + Float16Vector av = Float16Vector.fromArray(SPECIES, a, i); + Float16Vector bv = Float16Vector.fromArray(SPECIES, order, i); + bv.selectFrom(av, vmask).intoArray(r, i); + } + + assertSelectFromArraysEquals(r, a, order, mask, SPECIES.length()); + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleMiscellaneousFloat16VectorMaxTestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + int hash = shuffle.hashCode(); + int length = shuffle.length(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + assertEquals(length, SPECIES.length()); + } + } + + @Test(dataProvider = "shuffleProvider") + static void shuffleToStringFloat16VectorMaxTestsSmokeTest(BiFunction fs) { + int[] a = fs.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var shuffle = VectorShuffle.fromArray(SPECIES, a, i); + String str = shuffle.toString(); + + int subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + Assert.assertTrue(str.equals("Shuffle" + Arrays.toString(subarr)), "at index " + + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + } + } + + @Test(dataProvider = "shuffleCompareOpProvider") + static void shuffleEqualsFloat16VectorMaxTestsSmokeTest(BiFunction fa, BiFunction fb) { + int[] a = fa.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + int[] b = fb.apply(SPECIES.length() * BUFFER_REPS, SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = VectorShuffle.fromArray(SPECIES, a, i); + var bv = VectorShuffle.fromArray(SPECIES, b, i); + boolean eq = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(eq, Arrays.equals(a, i, to, b, i, to)); + } + } + + @Test(dataProvider = "boolMaskBinaryOpProvider") + static void maskEqualsFloat16VectorMaxTests(IntFunction fa, IntFunction fb) { + boolean[] a = fa.apply(SPECIES.length()); + boolean[] b = fb.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var av = SPECIES.loadMask(a, i); + var bv = SPECIES.loadMask(b, i); + boolean equals = av.equals(bv); + int to = i + SPECIES.length(); + assertEquals(equals, Arrays.equals(a, i, to, b, i, to)); + } + } + } + + @Test(dataProvider = "maskProvider") + static void maskHashCodeFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + int hash = vmask.hashCode(); + + boolean subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); + } + } + + static int maskTrueCount(boolean[] a, int idx) { + int trueCount = 0; + for (int i = idx; i < idx + SPECIES.length(); i++) { + trueCount += a[i] ? 1 : 0; + } + return trueCount; + } + + @Test(dataProvider = "maskProvider") + static void maskTrueCountFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.trueCount(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16VectorMaxTests::maskTrueCount); + } + + static int maskLastTrue(boolean[] a, int idx) { + int i = idx + SPECIES.length() - 1; + for (; i >= idx; i--) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskLastTrueFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.lastTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16VectorMaxTests::maskLastTrue); + } + + static int maskFirstTrue(boolean[] a, int idx) { + int i = idx; + for (; i < idx + SPECIES.length(); i++) { + if (a[i]) { + break; + } + } + return i - idx; + } + + @Test(dataProvider = "maskProvider") + static void maskFirstTrueFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + boolean[] a = fa.apply(SPECIES.length()); + int[] r = new int[a.length]; + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + r[i] = vmask.firstTrue(); + } + } + + assertMaskReductionArraysEquals(r, a, Float16VectorMaxTests::maskFirstTrue); + } + + @Test(dataProvider = "maskProvider") + static void maskCompressFloat16VectorMaxTestsSmokeTest(IntFunction fa) { + int trueCount = 0; + boolean[] a = fa.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT * INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + var vmask = SPECIES.loadMask(a, i); + trueCount = vmask.trueCount(); + var rmask = vmask.compress(); + for (int j = 0; j < SPECIES.length(); j++) { + assertEquals(rmask.laneIsSet(j), j < trueCount); + } + } + } + } + + @DataProvider + public static Object[][] offsetProvider() { + return new Object[][]{ + {0}, + {-1}, + {+1}, + {+2}, + {-2}, + }; + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeFloat16VectorMaxTestsSmokeTest(int offset) { + int limit = SPECIES.length() * BUFFER_REPS; + for (int i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + int index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @Test(dataProvider = "offsetProvider") + static void indexInRangeLongFloat16VectorMaxTestsSmokeTest(int offset) { + long limit = SPECIES.length() * BUFFER_REPS; + for (long i = 0; i < limit; i += SPECIES.length()) { + var actualMask = SPECIES.indexInRange(i + offset, limit); + var expectedMask = SPECIES.maskAll(true).indexInRange(i + offset, limit); + assert(actualMask.equals(expectedMask)); + for (int j = 0; j < SPECIES.length(); j++) { + long index = i + j + offset; + assertEquals(actualMask.laneIsSet(j), index >= 0 && index < limit); + } + } + } + + @DataProvider + public static Object[][] lengthProvider() { + return new Object[][]{ + {0}, + {1}, + {32}, + {37}, + {1024}, + {1024+1}, + {1024+5}, + }; + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundFloat16VectorMaxTestsSmokeTest(int length) { + int actualLoopBound = SPECIES.loopBound(length); + int expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test(dataProvider = "lengthProvider") + static void loopBoundLongFloat16VectorMaxTestsSmokeTest(int _length) { + long length = _length; + long actualLoopBound = SPECIES.loopBound(length); + long expectedLoopBound = length - Math.floorMod(length, SPECIES.length()); + assertEquals(actualLoopBound, expectedLoopBound); + } + + @Test + static void ElementSizeFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + int elsize = av.elementSize(); + assertEquals(elsize, Float16.SIZE); + } + + @Test + static void VectorShapeFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + assert(vsh.equals(VectorShape.S_Max_BIT)); + } + + @Test + static void ShapeWithLanesFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = vsh.withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void ElementTypeFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementType() == Float16.class); + } + + @Test + static void SpeciesElementSizeFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().elementSize() == Float16.SIZE); + } + + @Test + static void VectorTypeFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + assert(av.species().vectorType() == av.getClass()); + } + + @Test + static void WithLanesFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorSpecies species = av.species().withLanes(Float16.class); + assert(species.equals(SPECIES)); + } + + @Test + static void WithShapeFloat16VectorMaxTestsSmokeTest() { + Float16Vector av = Float16Vector.zero(SPECIES); + VectorShape vsh = av.shape(); + VectorSpecies species = av.species().withShape(vsh); + assert(species.equals(SPECIES)); + } + + @Test + static void MaskAllTrueFloat16VectorMaxTestsSmokeTest() { + for (int ic = 0; ic < INVOC_COUNT; ic++) { + assertEquals(SPECIES.maskAll(true).toLong(), -1L >>> (64 - SPECIES.length())); + } + } +} diff --git a/test/jdk/jdk/incubator/vector/ShortVector128Tests.java b/test/jdk/jdk/incubator/vector/ShortVector128Tests.java index 40de51c90ab..1573748b925 100644 --- a/test/jdk/jdk/incubator/vector/ShortVector128Tests.java +++ b/test/jdk/jdk/incubator/vector/ShortVector128Tests.java @@ -1719,9 +1719,14 @@ public class ShortVector128Tests extends AbstractVectorTest { assertEquals(asIntegral.species(), SPECIES); } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test void viewAsFloatingLanesTest() { - SPECIES.zero().viewAsFloatingLanes(); + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + VectorSpecies asFloatingSpecies = asFloating.species(); + Assert.assertNotEquals(asFloatingSpecies.elementType(), SPECIES.elementType()); + assertEquals(asFloatingSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asFloatingSpecies.length(), SPECIES.length()); + assertEquals(asFloating.viewAsIntegralLanes().species(), SPECIES); } @Test diff --git a/test/jdk/jdk/incubator/vector/ShortVector256Tests.java b/test/jdk/jdk/incubator/vector/ShortVector256Tests.java index c28dd34c8ef..059388477e8 100644 --- a/test/jdk/jdk/incubator/vector/ShortVector256Tests.java +++ b/test/jdk/jdk/incubator/vector/ShortVector256Tests.java @@ -1719,9 +1719,14 @@ public class ShortVector256Tests extends AbstractVectorTest { assertEquals(asIntegral.species(), SPECIES); } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test void viewAsFloatingLanesTest() { - SPECIES.zero().viewAsFloatingLanes(); + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + VectorSpecies asFloatingSpecies = asFloating.species(); + Assert.assertNotEquals(asFloatingSpecies.elementType(), SPECIES.elementType()); + assertEquals(asFloatingSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asFloatingSpecies.length(), SPECIES.length()); + assertEquals(asFloating.viewAsIntegralLanes().species(), SPECIES); } @Test diff --git a/test/jdk/jdk/incubator/vector/ShortVector512Tests.java b/test/jdk/jdk/incubator/vector/ShortVector512Tests.java index 703ef7d358b..3e39d1bc32f 100644 --- a/test/jdk/jdk/incubator/vector/ShortVector512Tests.java +++ b/test/jdk/jdk/incubator/vector/ShortVector512Tests.java @@ -1719,9 +1719,14 @@ public class ShortVector512Tests extends AbstractVectorTest { assertEquals(asIntegral.species(), SPECIES); } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test void viewAsFloatingLanesTest() { - SPECIES.zero().viewAsFloatingLanes(); + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + VectorSpecies asFloatingSpecies = asFloating.species(); + Assert.assertNotEquals(asFloatingSpecies.elementType(), SPECIES.elementType()); + assertEquals(asFloatingSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asFloatingSpecies.length(), SPECIES.length()); + assertEquals(asFloating.viewAsIntegralLanes().species(), SPECIES); } @Test diff --git a/test/jdk/jdk/incubator/vector/ShortVector64Tests.java b/test/jdk/jdk/incubator/vector/ShortVector64Tests.java index ea6846498a3..e78eb2ee283 100644 --- a/test/jdk/jdk/incubator/vector/ShortVector64Tests.java +++ b/test/jdk/jdk/incubator/vector/ShortVector64Tests.java @@ -1719,9 +1719,14 @@ public class ShortVector64Tests extends AbstractVectorTest { assertEquals(asIntegral.species(), SPECIES); } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test void viewAsFloatingLanesTest() { - SPECIES.zero().viewAsFloatingLanes(); + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + VectorSpecies asFloatingSpecies = asFloating.species(); + Assert.assertNotEquals(asFloatingSpecies.elementType(), SPECIES.elementType()); + assertEquals(asFloatingSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asFloatingSpecies.length(), SPECIES.length()); + assertEquals(asFloating.viewAsIntegralLanes().species(), SPECIES); } @Test diff --git a/test/jdk/jdk/incubator/vector/ShortVectorMaxTests.java b/test/jdk/jdk/incubator/vector/ShortVectorMaxTests.java index 6a85da12054..dbcb6912323 100644 --- a/test/jdk/jdk/incubator/vector/ShortVectorMaxTests.java +++ b/test/jdk/jdk/incubator/vector/ShortVectorMaxTests.java @@ -1725,9 +1725,14 @@ public class ShortVectorMaxTests extends AbstractVectorTest { assertEquals(asIntegral.species(), SPECIES); } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test void viewAsFloatingLanesTest() { - SPECIES.zero().viewAsFloatingLanes(); + Vector asFloating = SPECIES.zero().viewAsFloatingLanes(); + VectorSpecies asFloatingSpecies = asFloating.species(); + Assert.assertNotEquals(asFloatingSpecies.elementType(), SPECIES.elementType()); + assertEquals(asFloatingSpecies.vectorShape(), SPECIES.vectorShape()); + assertEquals(asFloatingSpecies.length(), SPECIES.length()); + assertEquals(asFloating.viewAsIntegralLanes().species(), SPECIES); } @Test diff --git a/test/jdk/jdk/incubator/vector/gen-tests.sh b/test/jdk/jdk/incubator/vector/gen-tests.sh index 8b5c4cac616..6890f4e1cdd 100644 --- a/test/jdk/jdk/incubator/vector/gen-tests.sh +++ b/test/jdk/jdk/incubator/vector/gen-tests.sh @@ -52,11 +52,11 @@ Log false "$compilation\n" Log true "done\n" # For each type -for type in byte short int long float double +for type in byte short int long float double float16 do Type="$(tr '[:lower:]' '[:upper:]' <<< ${type:0:1})${type:1}" TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${type})" - args="-K$type -Dtype=$type -DType=$Type -DTYPE=$TYPE" + args="-K$type -DType=$Type -DTYPE=$TYPE" Boxtype=$Type Wideboxtype=$Boxtype @@ -69,6 +69,7 @@ do bitstype=$type Bitstype=$Type Boxbitstype=$Boxtype + testtype=$type fptype=$type Fptype=$Type @@ -118,11 +119,23 @@ do MaxValue=POSITIVE_INFINITY MinValue=NEGATIVE_INFINITY ;; + float16) + kind=FP + fpkind=FP16 + bitstype=short + type=short + Bitstype=Short + Boxbitstype=Short + Wideboxtype=Float16 + MaxValue=POSITIVE_INFINITY + MinValue=NEGATIVE_INFINITY + Type=Float16 + ;; esac - args="$args -K$kind -K$fpkind -K$Type -DBoxtype=$Boxtype -DWideboxtype=$Wideboxtype -DMaxValue=$MaxValue -DMinValue=$MinValue" + args="$args -Dtype=$type -K$kind -K$Type -DBoxtype=$Boxtype -DWideboxtype=$Wideboxtype -DMaxValue=$MaxValue -DMinValue=$MinValue" args="$args -Dbitstype=$bitstype -DBitstype=$Bitstype -DBoxbitstype=$Boxbitstype" - args="$args -Dfptype=$fptype -DFptype=$Fptype -DBoxfptype=$Boxfptype" + args="$args -Dtesttype=$testtype -K$fpkind -Dfptype=$fptype -DFptype=$Fptype -DBoxfptype=$Boxfptype" abstractvectortype=${typeprefix}${Type}Vector abstractvectorteststype=${typeprefix}${Type}VectorTests diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Long-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Long-op.template index a4cea57471a..c48c658cdd8 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Long-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Long-op.template @@ -4,5 +4,5 @@ for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - av.lanewise(VectorOperators.[[TEST]], (long)b[i]).intoArray(r, i); + av.lanewise(VectorOperators.[[TEST]], {#if[FP16]?shortBitsToFloat16(b[i]).longValue():(long)b[i]}).intoArray(r, i); } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Masked-Long-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Masked-Long-op.template index 16343eeaf40..2531f048300 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Masked-Long-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Binary-Broadcast-Masked-Long-op.template @@ -6,5 +6,5 @@ for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - av.lanewise(VectorOperators.[[TEST]], (long)b[i], vmask).intoArray(r, i); + av.lanewise(VectorOperators.[[TEST]], {#if[FP16]?shortBitsToFloat16(b[i]).longValue():(long)b[i]}, vmask).intoArray(r, i); } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Compare-Broadcast.template b/test/jdk/jdk/incubator/vector/templates/Unit-Compare-Broadcast.template index faae74426e7..b59fd1a793f 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Compare-Broadcast.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Compare-Broadcast.template @@ -43,11 +43,11 @@ for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - VectorMask<$Wideboxtype$> mv = av.compare(VectorOperators.[[TEST]], (long)b[i]); + VectorMask<$Wideboxtype$> mv = av.compare(VectorOperators.[[TEST]], {#if[FP16]?shortBitsToFloat16(b[i]).longValue():(long)b[i]}); // Check results as part of computation. for (int j = 0; j < SPECIES.length(); j++) { - assertEquals(mv.laneIsSet(j), [[TEST_OP]](a[i + j], ($type$)((long)b[i]))); + assertEquals(mv.laneIsSet(j), [[TEST_OP]](a[i + j], {#if[FP16]?float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())):($type$)((long)b[i])})); } } } @@ -63,11 +63,11 @@ for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - VectorMask<$Wideboxtype$> mv = av.compare(VectorOperators.[[TEST]], (long)b[i], vmask); + VectorMask<$Wideboxtype$> mv = av.compare(VectorOperators.[[TEST]], {#if[FP16]?shortBitsToFloat16(b[i]).longValue():(long)b[i]}, vmask); // Check results as part of computation. for (int j = 0; j < SPECIES.length(); j++) { - assertEquals(mv.laneIsSet(j), mask[j] && ([[TEST_OP]](a[i + j], ($type$)((long)b[i])))); + assertEquals(mv.laneIsSet(j), mask[j] && ([[TEST_OP]](a[i + j], {#if[FP16]?float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[i]).longValue())):($type$)((long)b[i])}))); } } } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template index 5ad7623d2c0..b673e5e1fab 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template @@ -87,7 +87,12 @@ String str = av.toString(); $type$ subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); +#if[FP16] + String expectedStr = Arrays.toString(toFloat16Array(subarr)); +#else[FP16] + String expectedStr = Arrays.toString(subarr); +#end[FP16] + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } @@ -100,11 +105,25 @@ int hash = av.hashCode(); $type$ subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); +#if[FP16] + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); +#else[FP16] int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); +#end[FP16] Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } +#if[FP16] + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + +#end[FP16] #if[byte] @Test(dataProvider = "$type$UnaryOpProvider") static void reinterpretAsBytes$vectorteststype$SmokeTest(IntFunction<$type$[]> fa) { @@ -210,7 +229,7 @@ $type$[] r = new $type$[a.length]; for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$.broadcast(SPECIES, (long)a[i]).intoArray(r, i); + $abstractvectortype$.broadcast(SPECIES, {#if[FP16]?shortBitsToFloat16(a[i]).longValue():(long)a[i]}).intoArray(r, i); } assertBroadcastArraysEquals(r, a); } @@ -227,7 +246,7 @@ for (int ic = 0; ic < INVOC_COUNT; ic++) { for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - av.blend((long)b[i], vmask).intoArray(r, i); + av.blend({#if[FP16]?shortBitsToFloat16(b[i]).longValue():(long)b[i]}, vmask).intoArray(r, i); } } assertBroadcastLongArraysEquals(r, a, b, mask, $vectorteststype$::blend); @@ -588,14 +607,14 @@ static void ShapeWithLanes$vectorteststype$SmokeTest() { $abstractvectortype$ av = $abstractvectortype$.zero(SPECIES); VectorShape vsh = av.shape(); - VectorSpecies species = vsh.withLanes($type$.class); + VectorSpecies species = vsh.withLanes({#if[FP16]?Float16.class:$type$.class}); assert(species.equals(SPECIES)); } @Test static void ElementType$vectorteststype$SmokeTest() { $abstractvectortype$ av = $abstractvectortype$.zero(SPECIES); - assert(av.species().elementType() == $type$.class); + assert(av.species().elementType() == {#if[FP16]?Float16.class:$type$.class}); } @Test @@ -613,7 +632,7 @@ @Test static void WithLanes$vectorteststype$SmokeTest() { $abstractvectortype$ av = $abstractvectortype$.zero(SPECIES); - VectorSpecies species = av.species().withLanes($type$.class); + VectorSpecies species = av.species().withLanes({#if[FP16]?Float16.class:$type$.class}); assert(species.equals(SPECIES)); } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index b87012a2181..7047e27b797 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -48,6 +48,11 @@ import jdk.incubator.vector.ByteVector; #if[Float] import jdk.incubator.vector.FloatVector; #end[Float] +#if[FP16] +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.*; +import jdk.incubator.vector.Float16Vector; +#end[FP16] #if[Int] import jdk.incubator.vector.IntVector; #end[Int] @@ -86,6 +91,38 @@ public class $vectorteststype$ extends AbstractVectorTest { #end[MaxBit] static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); +#if[FP16] + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + static void assertEquals(short actual, short expected, short delta) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta)); + } + static void assertEquals(short actual, short expected, short delta, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), Float.float16ToFloat(delta), msg); + } + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } + static void assertEquals(long actual, long expected) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected)); + } + static void assertEquals(long actual, long expected, String msg) { + Assert.assertEquals(Float.float16ToFloat((short)actual), Float.float16ToFloat((short)expected), msg); + } +#else[FP16] static void assertEquals($type$ actual, $type$ expected) { Assert.assertEquals(actual, expected); } @@ -112,6 +149,7 @@ public class $vectorteststype$ extends AbstractVectorTest { Assert.assertEquals(actual, expected, msg); } #end[!long] +#end[FP16] static void assertEquals(String actual, String expected) { Assert.assertEquals(actual, expected); } @@ -152,9 +190,9 @@ public class $vectorteststype$ extends AbstractVectorTest { private static final $type$ AND_IDENTITY = ($type$)-1; #end[BITWISE] private static final $type$ FIRST_NONZERO_IDENTITY = ($type$)0; - private static final $type$ MAX_IDENTITY = $Wideboxtype$.$MinValue$; - private static final $type$ MIN_IDENTITY = $Wideboxtype$.$MaxValue$; - private static final $type$ MUL_IDENTITY = ($type$)1; + private static final $type$ MAX_IDENTITY = {#if[FP16]?float16ToRawShortBits($Wideboxtype$.$MinValue$):$Wideboxtype$.$MinValue$}; + private static final $type$ MIN_IDENTITY = {#if[FP16]?float16ToRawShortBits($Wideboxtype$.$MaxValue$):$Wideboxtype$.$MaxValue$}; + private static final $type$ MUL_IDENTITY = {#if[FP16]?float16ToRawShortBits(Float16.valueOf(1.0f)):($type$)1}; #if[BITWISE] private static final $type$ OR_IDENTITY = ($type$)0; private static final $type$ SUADD_IDENTITY = ($type$)0; @@ -165,10 +203,10 @@ public class $vectorteststype$ extends AbstractVectorTest { #if[FP] // for floating point addition reduction ops that may introduce rounding errors - private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_ADD = ($type$)10.0; + private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_ADD = {#if[FP16]?float16ToRawShortBits(Float16.valueOf(10.0f)):($type$)10.0}; // for floating point multiplication reduction ops that may introduce rounding errors - private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_MUL = ($type$)50.0; + private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_MUL = {#if[FP16]?float16ToRawShortBits(Float16.valueOf(50.0f)):($type$)50.0}; #end[FP] static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / $bits$); @@ -176,8 +214,8 @@ public class $vectorteststype$ extends AbstractVectorTest { static void assertArraysStrictlyEquals($type$[] r, $type$[] a) { for (int i = 0; i < a.length; i++) { #if[FP] - $bitstype$ ir = $Wideboxtype$.$type$ToRaw$Bitstype$Bits(r[i]); - $bitstype$ ia = $Wideboxtype$.$type$ToRaw$Bitstype$Bits(a[i]); + $bitstype$ ir = {#if[FP16]?r[i]:$Wideboxtype$.$type$ToRaw$Bitstype$Bits(r[i])}; + $bitstype$ ia = {#if[FP16]?a[i]:$Wideboxtype$.$type$ToRaw$Bitstype$Bits(a[i])}; if (ir != ia) { #if[Float] Assert.fail(String.format("at index #%d, expected = %08X, actual = %08X", i, ia, ir)); @@ -265,6 +303,23 @@ public class $vectorteststype$ extends AbstractVectorTest { #end[FP] } #if[FP] +#if[FP16] + + static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, + FReductionOp f, FReductionAllOp fa, + $type$ relativeErrorFactor) { + int i = 0; + try { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor)))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor)))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(rc)), shortBitsToFloat16(relativeErrorFactor))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i), float16ToRawShortBits(Float16.multiply(Float16.ulp(shortBitsToFloat16(r[i])), shortBitsToFloat16(relativeErrorFactor))), "at index #" + i); + } + } +#else[FP16] static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, FReductionOp f, FReductionAllOp fa, @@ -280,6 +335,7 @@ public class $vectorteststype$ extends AbstractVectorTest { assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } +#end[FP16] #end[FP] interface FReductionMaskedOp { @@ -308,6 +364,23 @@ public class $vectorteststype$ extends AbstractVectorTest { #end[FP] } #if[FP] +#if[FP16] + + static void assertReductionArraysEqualsMasked($type$[] r, $type$ rc, $type$[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + $type$ relativeError) { + int i = 0; + try { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError))))); + for (; i < a.length; i += SPECIES.length()) { + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError))))); + } + } catch (AssertionError e) { + assertEquals(rc, fa.apply(a, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(rc), shortBitsToFloat16(relativeError)))), "Final result is incorrect!"); + assertEquals(r[i], f.apply(a, i, mask), float16ToRawShortBits(Float16.abs(Float16.multiply(shortBitsToFloat16(r[i]), shortBitsToFloat16(relativeError)))), "at index #" + i); + } + } +#else[FP16] static void assertReductionArraysEqualsMasked($type$[] r, $type$ rc, $type$[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa, @@ -324,6 +397,7 @@ relativeError)); assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } +#end[FP16] #end[FP] #if[!Long] @@ -473,7 +547,7 @@ relativeError)); for (; i < a.length; i += vector_len) { for (j = 0; j < vector_len; j++) { idx = i + j; - wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + wrapped_index = Math.floorMod({#if[FP16]?shortBitsToFloat16(order[idx]).intValue():(int)order[idx]}, 2 * vector_len); is_exceptional_idx = wrapped_index >= vector_len; oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); @@ -489,12 +563,12 @@ relativeError)); try { for (; i < a.length; i += vector_len) { for (j = 0; j < vector_len; j++) { - assertEquals(r[i+j], a[i+(int)order[i+j]]); + assertEquals(r[i+j], a[i+{#if[FP16]?shortBitsToFloat16(order[i+j]).intValue():(int)order[i+j]}]); } } } catch (AssertionError e) { int idx = i + j; - assertEquals(r[i+j], a[i+(int)order[i+j]], "at index #" + idx + ", input = " + a[i+(int)order[i+j]]); + assertEquals(r[i+j], a[i+{#if[FP16]?shortBitsToFloat16(order[i+j]).intValue():(int)order[i+j]}], "at index #" + idx + ", input = " + a[i+{#if[FP16]?shortBitsToFloat16(order[i+j]).intValue():(int)order[i+j]}]); } } @@ -524,7 +598,7 @@ relativeError)); for (; i < a.length; i += vector_len) { for (j = 0; j < vector_len; j++) { if (mask[j % SPECIES.length()]) - assertEquals(r[i+j], a[i+(int)order[i+j]]); + assertEquals(r[i+j], a[i+{#if[FP16]?shortBitsToFloat16(order[i+j]).intValue():(int)order[i+j]}]); else assertEquals(r[i+j], ($type$)0); } @@ -532,7 +606,7 @@ relativeError)); } catch (AssertionError e) { int idx = i + j; if (mask[j % SPECIES.length()]) - assertEquals(r[i+j], a[i+(int)order[i+j]], "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); + assertEquals(r[i+j], a[i+{#if[FP16]?shortBitsToFloat16(order[i+j]).intValue():(int)order[i+j]}], "at index #" + idx + ", input = " + a[i+{#if[FP16]?shortBitsToFloat16(order[i+j]).intValue():(int)order[i+j]}] + ", mask = " + mask[j % SPECIES.length()]); else assertEquals(r[i+j], ($type$)0, "at index #" + idx + ", input = " + a[i+(int)order[i+j]] + ", mask = " + mask[j % SPECIES.length()]); } @@ -681,10 +755,10 @@ relativeError)); int i = 0; try { for (; i < a.length; i++) { - assertEquals(r[i], f.apply(a[i], ($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()]))); + assertEquals(r[i], f.apply(a[i], {#if[FP16]?float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())):($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()])})); } } catch (AssertionError e) { - assertEquals(r[i], f.apply(a[i], ($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()])), + assertEquals(r[i], f.apply(a[i], {#if[FP16]?float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())):($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()])}), "(" + a[i] + ", " + b[(i / SPECIES.length()) * SPECIES.length()] + ") at index #" + i); } } @@ -745,10 +819,10 @@ relativeError)); int i = 0; try { for (; i < a.length; i++) { - assertEquals(r[i], f.apply(a[i], ($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()]), mask[i % SPECIES.length()])); + assertEquals(r[i], f.apply(a[i], {#if[FP16]?float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())):($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()])}, mask[i % SPECIES.length()])); } } catch (AssertionError err) { - assertEquals(r[i], f.apply(a[i], ($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()]), + assertEquals(r[i], f.apply(a[i], {#if[FP16]?float16ToRawShortBits(Float16.valueOf(shortBitsToFloat16(b[(i / SPECIES.length()) * SPECIES.length()]).longValue())):($type$)((long)b[(i / SPECIES.length()) * SPECIES.length()])}, mask[i % SPECIES.length()]), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", mask = " + mask[i % SPECIES.length()]); @@ -978,6 +1052,26 @@ relativeError)); #if[FP] static boolean isWithin1Ulp($type$ actual, $type$ expected) { +#if[FP16] + Float16 act = shortBitsToFloat16(actual); + Float16 exp = shortBitsToFloat16(expected); + if (Float16.isNaN(exp) && !Float16.isNaN(act)) { + return false; + } else if (!Float16.isNaN(exp) && Float16.isNaN(act)) { + return false; + } + + Float16 low = Float16.nextDown(exp); + Float16 high = Float16.nextUp(exp); + + if (Float16.compare(low, exp) > 0) { + return false; + } + + if (Float16.compare(high, exp) < 0) { + return false; + } +#else[FP16] if ($Type$.isNaN(expected) && !$Type$.isNaN(actual)) { return false; } else if (!$Type$.isNaN(expected) && $Type$.isNaN(actual)) { @@ -994,6 +1088,7 @@ relativeError)); if ($Type$.compare(high, expected) < 0) { return false; } +#end[FP16] return true; } @@ -1003,11 +1098,11 @@ relativeError)); try { // Check that result is within 1 ulp of strict math or equivalent to math implementation. for (; i < a.length; i++) { - Assert.assertTrue($Type$.compare(r[i], mathf.apply(a[i])) == 0 || + Assert.assertTrue($Wideboxtype$.compare({#if[FP16]?shortBitsToFloat16(r[i]):r[i]}, {#if[FP16]?shortBitsToFloat16(mathf.apply(a[i])):mathf.apply(a[i])}) == 0 || isWithin1Ulp(r[i], strictmathf.apply(a[i]))); } } catch (AssertionError e) { - Assert.assertTrue($Type$.compare(r[i], mathf.apply(a[i])) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); + Assert.assertTrue($Wideboxtype$.compare({#if[FP16]?shortBitsToFloat16(r[i]):r[i]}, {#if[FP16]?shortBitsToFloat16(mathf.apply(a[i])):mathf.apply(a[i])}) == 0, "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i])); Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i])), "at index #" + i + ", input = " + a[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i])); } } @@ -1017,11 +1112,11 @@ relativeError)); try { // Check that result is within 1 ulp of strict math or equivalent to math implementation. for (; i < a.length; i++) { - Assert.assertTrue($Type$.compare(r[i], mathf.apply(a[i], b[i])) == 0 || + Assert.assertTrue($Wideboxtype$.compare({#if[FP16]?shortBitsToFloat16(r[i]):r[i]}, {#if[FP16]?shortBitsToFloat16(mathf.apply(a[i], b[i])):mathf.apply(a[i], b[i])}) == 0 || isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i]))); } } catch (AssertionError e) { - Assert.assertTrue($Type$.compare(r[i], mathf.apply(a[i], b[i])) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); + Assert.assertTrue($Wideboxtype$.compare({#if[FP16]?shortBitsToFloat16(r[i]):r[i]}, {#if[FP16]?shortBitsToFloat16(mathf.apply(a[i], b[i])):mathf.apply(a[i], b[i])}) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[i])); Assert.assertTrue(isWithin1Ulp(r[i], strictmathf.apply(a[i], b[i])), "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[i] + ", actual = " + r[i] + ", expected (within 1 ulp) = " + strictmathf.apply(a[i], b[i])); } } @@ -1032,14 +1127,14 @@ relativeError)); try { // Check that result is within 1 ulp of strict math or equivalent to math implementation. for (; i < a.length; i++) { - Assert.assertTrue($Type$.compare(r[i], - mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])) == 0 || + Assert.assertTrue($Wideboxtype$.compare({#if[FP16]?shortBitsToFloat16(r[i]):r[i]}, + {#if[FP16]?shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])):mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])}) == 0 || isWithin1Ulp(r[i], strictmathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()]))); } } catch (AssertionError e) { - Assert.assertTrue($Type$.compare(r[i], - mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])) == 0, + Assert.assertTrue($Wideboxtype$.compare({#if[FP16]?shortBitsToFloat16(r[i]):r[i]}, + {#if[FP16]?shortBitsToFloat16(mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])):mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])}) == 0, "at index #" + i + ", input1 = " + a[i] + ", input2 = " + b[(i / SPECIES.length()) * SPECIES.length()] + ", actual = " + r[i] + ", expected = " + mathf.apply(a[i], b[(i / SPECIES.length()) * SPECIES.length()])); @@ -1236,6 +1331,7 @@ relativeError)); #if[!Int] #if[!byteOrShort] +#if[!FP16] static int intCornerCaseValue(int i) { switch(i % 5) { case 0: @@ -1250,7 +1346,45 @@ relativeError)); return (int)0; } } +#end[!FP16] +#if[FP16] + static $type$ convToFloat16(int i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static $type$ convIntToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504); + case 1: + return convToFloat16(-65504); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> INT_$TYPE$_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> ((($type$)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[intCornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convIntToFloat16CornerCases(i)); + }) + ); +#else[FP16] static final List> INT_$TYPE$_GENERATORS = List.of( withToString("$type$[-i * 5]", (int s) -> { return fill(s * BUFFER_REPS, @@ -1269,6 +1403,7 @@ relativeError)); i -> ($type$)intCornerCaseValue(i)); }) ); +#end[FP16] #end[!byteOrShort] #end[!Int] @@ -1276,7 +1411,11 @@ relativeError)); int i = 0; try { for (; i < r.length; i++) { +#if[FP16] + assertEquals(r[i], (int)Float.float16ToFloat(a[i+offs])); +#else[FP16] assertEquals(r[i], (int)(a[i+offs])); +#end[FP16] } } catch (AssertionError e) { assertEquals(r[i], (int)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); @@ -1285,6 +1424,7 @@ relativeError)); #if[!Long] #if[FP] +#if[!FP16] static long longCornerCaseValue(int i) { switch(i % 5) { case 0: @@ -1299,7 +1439,45 @@ relativeError)); return (long)0; } } +#end[!FP16] +#if[FP16] + static $type$ convToFloat16(long i) { + return float16ToRawShortBits(Float16.valueOf(i)); + } + + static $type$ convLongToFloat16CornerCases(int i) { + switch(i % 4) { + case 0: + return convToFloat16(65504L); + case 1: + return convToFloat16(-65504L); + case 2: + return float16ToRawShortBits(Float16.valueOf(-0.0f)); + default: + return float16ToRawShortBits(Float16.valueOf(0.0f)); + } + } + + static final List> LONG_$TYPE$_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> ((($type$)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convLongToFloat16CornerCases(i)); + }) + ); +#else[FP16] static final List> LONG_$TYPE$_GENERATORS = List.of( withToString("$type$[-i * 5]", (int s) -> { return fill(s * BUFFER_REPS, @@ -1318,6 +1496,7 @@ relativeError)); i -> ($type$)longCornerCaseValue(i)); }) ); +#end[FP16] #end[FP] #end[!Long] @@ -1338,7 +1517,11 @@ relativeError)); int i = 0; try { for (; i < r.length; i++) { +#if[FP16] + assertEquals(r[i], (long)Float.float16ToFloat(a[i+offs])); +#else[FP16] assertEquals(r[i], (long)(a[i+offs])); +#end[FP16] } } catch (AssertionError e) { assertEquals(r[i], (long)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); @@ -1350,7 +1533,11 @@ relativeError)); int i = 0; try { for (; i < r.length; i++) { +#if[FP16] + assertEquals(r[i], (double)Float.float16ToFloat(a[i+offs])); +#else[FP16] assertEquals(r[i], (double)(a[i+offs])); +#end[FP16] } } catch (AssertionError e) { assertEquals(r[i], (double)(a[i+offs]), "at index #" + i + ", input = " + a[i+offs]); @@ -1358,8 +1545,40 @@ relativeError)); } #end[!Double] +#if[FP16] static $bitstype$ bits($type$ e) { - return {#if[FP]? $Type$.$type$To$Bitstype$Bits(e): e}; + return e; + } + + static final List> $TYPE$_GENERATORS = List.of( + withToString("float16[-i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(-i * 5)); + }), + withToString("float16[i * 5]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> convToFloat16(i * 5)); + }), + withToString("float16[i + 1]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> ((($type$)(i + 1) == 0) ? convToFloat16(1) : convToFloat16(i + 1))); + }), + withToString("float16[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01f + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (i % 17 == 0) ? cornerCaseValue(i) : Float.floatToFloat16((0.01f + ((float)i / (i + 1))))); + }), + withToString("float16[cornerCaseValue(i)]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> cornerCaseValue(i)); + }) + ); +#else[FP16] + static $bitstype$ bits($type$ e) { + return {#if[FP]?$Wideboxtype$.$type$To$Bitstype$Bits(e):e}; } static final List> $TYPE$_GENERATORS = List.of( @@ -1394,6 +1613,7 @@ relativeError)); i -> cornerCaseValue(i)); }) ); +#end[FP16] #if[!FP] static final List> $TYPE$_SATURATING_GENERATORS = List.of( @@ -1472,7 +1692,7 @@ relativeError)); collect(Collectors.toList()); static final List> SELECT_FROM_INDEX_GENERATORS = List.of( - withToString("$type$[0..VECLEN*2)", (int s) -> { + withToString("$testtype$[0..VECLEN*2)", (int s) -> { return fill(s * BUFFER_REPS, i -> ($type$)(RAND.nextInt())); }) @@ -1709,27 +1929,27 @@ relativeError)); #end[!Int] static final List> $TYPE$_COMPARE_GENERATORS = List.of( - withToString("$type$[i]", (int s) -> { + withToString("$testtype$[i]", (int s) -> { return fill(s * BUFFER_REPS, i -> ($type$)i); }), - withToString("$type$[i - length / 2]", (int s) -> { + withToString("$testtype$[i - length / 2]", (int s) -> { return fill(s * BUFFER_REPS, i -> ($type$)(i - (s * BUFFER_REPS / 2))); }), - withToString("$type$[i + 1]", (int s) -> { + withToString("$testtype$[i + 1]", (int s) -> { return fill(s * BUFFER_REPS, i -> ($type$)(i + 1)); }), - withToString("$type$[i - 2]", (int s) -> { + withToString("$testtype$[i - 2]", (int s) -> { return fill(s * BUFFER_REPS, i -> ($type$)(i - 2)); }), - withToString("$type$[zigZag(i)]", (int s) -> { + withToString("$testtype$[zigZag(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> i%3 == 0 ? ($type$)i : (i%3 == 1 ? ($type$)(i + 1) : ($type$)(i - 2))); }), - withToString("$type$[cornerCaseValue(i)]", (int s) -> { + withToString("$testtype$[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); }) @@ -1792,6 +2012,20 @@ relativeError)); static $type$ cornerCaseValue(int i) { #if[FP] +#if[FP16] + return switch(i % 10) { + case 0 -> float16ToRawShortBits($Wideboxtype$.MAX_VALUE); + case 1 -> float16ToRawShortBits($Wideboxtype$.MIN_VALUE); + case 2 -> float16ToRawShortBits($Wideboxtype$.NEGATIVE_INFINITY); + case 3 -> float16ToRawShortBits($Wideboxtype$.POSITIVE_INFINITY); + case 4 -> float16ToRawShortBits($Wideboxtype$.NaN); + case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); + default -> float16ToShortBits(Float16.valueOf(-0.0f)); + }; +#else[FP16] return switch(i % 8) { case 0 -> $Wideboxtype$.MAX_VALUE; case 1 -> $Wideboxtype$.MIN_VALUE; @@ -1806,6 +2040,7 @@ relativeError)); case 6 -> ($type$)0.0; default -> ($type$)-0.0; }; +#end[FP16] #else[FP] switch(i % 5) { case 0: @@ -1923,6 +2158,262 @@ relativeError)); #end[intOrLong] } #end[BITWISE] +#if[FP16] + + static boolean eq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() == bt.floatValue(); + } + + static boolean neq(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() != bt.floatValue(); + } + + static boolean lt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() < bt.floatValue(); + } + + static boolean le(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() <= bt.floatValue(); + } + + static boolean gt(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() > bt.floatValue(); + } + + static boolean ge(short a, short b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return at.floatValue() >= bt.floatValue(); + } + + static $type$ firstNonZero($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 zero = shortBitsToFloat16((short)0); + return Float16.compare(at, zero) != 0 ? a : b; + } + + static $type$ scalar_add($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.add(at, bt)); + } + + static $type$ scalar_sub($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.subtract(at, bt)); + } + + static $type$ scalar_mul($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.multiply(at, bt)); + + } + static $type$ scalar_max($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.max(at, bt)); + } + + static $type$ scalar_min($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.min(at, bt)); + } + + static $type$ scalar_div($type$ a, $type$ b) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + return float16ToRawShortBits(Float16.divide(at, bt)); + } + + static $type$ scalar_fma($type$ a, $type$ b, $type$ c) { + Float16 at = shortBitsToFloat16(a); + Float16 bt = shortBitsToFloat16(b); + Float16 ct = shortBitsToFloat16(c); + return float16ToRawShortBits(Float16.fma(at, bt, ct)); + } + + static $type$ scalar_abs($type$ a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.abs(at)); + } + + static $type$ scalar_neg($type$ a) { + Float16 at = shortBitsToFloat16(a); + return float16ToRawShortBits(Float16.negate(at)); + } + + static $type$ scalar_sin($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_exp($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_log1p($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_log($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.log(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_log10($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_expm1($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_cos($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_tan($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_sinh($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_cosh($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_tanh($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_asin($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_acos($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_atan($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_cbrt($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_sqrt($type$ a) { + return float16ToRawShortBits(Float16.valueOf(Math.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ scalar_hypot($type$ a, $type$ b) { + return float16ToRawShortBits(Float16.valueOf(Math.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static $type$ scalar_pow($type$ a, $type$ b) { + return float16ToRawShortBits(Float16.valueOf(Math.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static $type$ scalar_atan2($type$ a, $type$ b) { + return float16ToRawShortBits(Float16.valueOf(Math.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static $type$ strict_scalar_sin($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sin(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_exp($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.exp(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_log1p($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log1p(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_log($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_log10($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.log10(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_expm1($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.expm1(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_cos($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cos(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_tan($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tan(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_sinh($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sinh(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_cosh($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cosh(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_tanh($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.tanh(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_asin($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.asin(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_acos($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.acos(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_atan($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_cbrt($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.cbrt(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_sqrt($type$ a) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.sqrt(shortBitsToFloat16(a).doubleValue()))); + } + + static $type$ strict_scalar_hypot($type$ a, $type$ b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.hypot(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static $type$ strict_scalar_pow($type$ a, $type$ b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.pow(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } + + static $type$ strict_scalar_atan2($type$ a, $type$ b) { + return float16ToRawShortBits(Float16.valueOf(StrictMath.atan2(shortBitsToFloat16(a).doubleValue(), + shortBitsToFloat16(b).doubleValue()))); + } +#else[FP16] static boolean eq($type$ a, $type$ b) { return a == b; @@ -2155,6 +2646,7 @@ relativeError)); return ($type$)StrictMath.atan2((double)a, (double)b); } #end[!BITWISE] +#end[FP16] #if[!FP] static boolean ult($type$ a, $type$ b) { @@ -2175,6 +2667,17 @@ relativeError)); #end[!FP] #if[FP] +#if[FP16] + static boolean isNaN($type$ a) { + return $Wideboxtype$.isNaN(shortBitsToFloat16(a)); + } + static boolean isFinite($type$ a) { + return $Wideboxtype$.isFinite(shortBitsToFloat16(a)); + } + static boolean isInfinite($type$ a) { + return $Wideboxtype$.isInfinite(shortBitsToFloat16(a)); + } +#else[FP16] static boolean isNaN($type$ a) { return $Wideboxtype$.isNaN(a); } @@ -2184,14 +2687,15 @@ relativeError)); static boolean isInfinite($type$ a) { return $Wideboxtype$.isInfinite(a); } +#end[FP16] #end[FP] @Test static void smokeTest1() { - $abstractvectortype$ three = $abstractvectortype$.broadcast(SPECIES, (byte)-3); - $abstractvectortype$ three2 = ($abstractvectortype$) SPECIES.broadcast(-3); + $abstractvectortype$ three = $abstractvectortype$.broadcast(SPECIES, {#if[FP16]?float16ToRawShortBits(Float16.valueOf(-3)):(byte)-3}); + $abstractvectortype$ three2 = ($abstractvectortype$) SPECIES.broadcast({#if[FP16]?Float16.valueOf(-3).longValue():-3}); assert(three.eq(three2).allTrue()); - $abstractvectortype$ three3 = three2.broadcast(1).broadcast(-3); + $abstractvectortype$ three3 = three2.broadcast({#if[FP16]?float16ToRawShortBits(Float16.valueOf(1)):1}).broadcast({#if[FP16]?Float16.valueOf(-3).longValue():-3}); assert(three.eq(three3).allTrue()); int scale = 2; Class ETYPE = $type$.class; @@ -2202,14 +2706,18 @@ relativeError)); $abstractvectortype$ higher = three.addIndex(scale); VectorMask<$Boxtype$> m = three.compare(VectorOperators.LE, higher); assert(m.allTrue()); - m = higher.min(($type$)-1).test(VectorOperators.IS_NEGATIVE); + m = higher.min({#if[FP16]?float16ToRawShortBits(Float16.valueOf(-1)):($type$)-1}).test(VectorOperators.IS_NEGATIVE); assert(m.allTrue()); #if[FP] m = higher.test(VectorOperators.IS_FINITE); assert(m.allTrue()); #end[FP] $type$ max = higher.reduceLanes(VectorOperators.MAX); +#if[FP16] + assert(max == float16ToRawShortBits(Float16.add(Float16.valueOf(-3), Float16.multiply(Float16.valueOf(scale), Float16.valueOf((SPECIES.length()-1)))))); +#else[FP16] assert(max == -3 + scale * (SPECIES.length()-1)); +#end[FP16] } private static $type$[] @@ -2286,13 +2794,13 @@ relativeError)); assertEquals(asFloating.species(), SPECIES); } #else[FP] -#if[byteOrShort] +#if[byte] @Test(expectedExceptions = UnsupportedOperationException.class) void viewAsFloatingLanesTest() { SPECIES.zero().viewAsFloatingLanes(); } -#else[byteOrShort] +#else[byte] @Test void viewAsFloatingLanesTest() { @@ -2303,7 +2811,7 @@ relativeError)); assertEquals(asFloatingSpecies.length(), SPECIES.length()); assertEquals(asFloating.viewAsIntegralLanes().species(), SPECIES); } -#end[byteOrShort] +#end[byte] #end[FP] #if[BITWISE] diff --git a/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template b/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template index 6779c78a490..2168eb95d30 100644 --- a/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template +++ b/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template @@ -41,6 +41,10 @@ import java.lang.foreign.MemorySegment; import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; +#if[FP16] +import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; +#end[FP16] import jdk.incubator.vector.$Type$Vector; import jdk.incubator.vector.VectorMask; #if[MaxBit] @@ -69,7 +73,11 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); +#if[FP16] + static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); +#else[FP16] static final ValueLayout.Of$Type$ ELEMENT_LAYOUT = ValueLayout.JAVA_$TYPE$.withByteAlignment(1); +#end[FP16] #if[MaxBit] static VectorShape getMaxBit() { @@ -81,6 +89,29 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / $bits$); +#if[FP16] + static void assertEquals(short actual, short expected) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected)); + } + + static void assertEquals(short actual, short expected, String msg) { + Assert.assertEquals(Float.float16ToFloat(actual), Float.float16ToFloat(expected), msg); + } + + static void assertEquals(short [] actual, short [] expected) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i])); + } + } + + static void assertEquals(short [] actual, short [] expected, String msg) { + assert actual.length == expected.length; + for (int i = 0; i < actual.length; i++) { + Assert.assertEquals(Float.float16ToFloat(actual[i]), Float.float16ToFloat(expected[i]), msg); + } + } +#else[FP16] static void assertEquals($type$ actual, $type$ expected) { Assert.assertEquals(actual, expected); } @@ -95,6 +126,7 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { static void assertEquals($type$ [] actual, $type$ [] expected, String msg) { Assert.assertEquals(actual, expected, msg); } +#end[FP16] static void assertArraysEquals($type$[] r, $type$[] a, boolean[] mask) { int i = 0; diff --git a/test/jdk/jdk/internal/jimage/ImageReaderTest.java b/test/jdk/jdk/internal/jimage/ImageReaderTest.java index 5104bb97f95..0fadc6eec12 100644 --- a/test/jdk/jdk/internal/jimage/ImageReaderTest.java +++ b/test/jdk/jdk/internal/jimage/ImageReaderTest.java @@ -46,6 +46,7 @@ import java.util.Set; import java.util.stream.Collectors; import static java.util.stream.Collectors.toSet; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -59,7 +60,7 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; /* * @test - * @bug 8385355 + * @bug 8385355 8386842 * @summary Tests for ImageReader. * @modules java.base/jdk.internal.jimage * jdk.jlink/jdk.tools.jlink.internal @@ -86,6 +87,11 @@ public class ImageReaderTest { "!META-INF/z", "!META-INF/collision/child.properties", "!META-INF/collision", + // Non-class resource in top level directory + "!fileA.txt", + "!fileB.txt", + "!META-INF/preview/fileA.txt", + "!META-INF/preview/fileB.txt", // Replaces original class in preview mode. "@com.foo.HasPreviewVersion", // New class in existing package in preview mode. @@ -96,6 +102,11 @@ public class ImageReaderTest { // Two new packages in preview mode (new symbolic links). "@com.bar.preview.stuff.Foo", "@com.bar.preview.stuff.Bar"), + "modbaz", Arrays.asList( + "!file.txt", + "!normal.txt", + "!META-INF/preview/file.txt", + "!META-INF/preview/previewOnly.txt"), "modgus", Arrays.asList( // A second module with a preview-only empty package (preview). "@com.bar.preview.other.Gus")); @@ -270,6 +281,18 @@ public class ImageReaderTest { assertAbsent(reader, "/modules/modfoo/com/foo/bar/IsPreviewOnly.class"); assertDirContents(reader, "/modules/modfoo/com/foo", "HasPreviewVersion.class", "NormalFoo.class", "bar"); assertDirContents(reader, "/modules/modfoo/com/foo/bar", "NormalBar.class"); + + // Non-class resource in top level directory + assertResource(reader, "modfoo", "fileA.txt"); + assertNonPreviewResourceVersion(reader, "modfoo", "fileA.txt"); + assertNode(reader, "/modules/modfoo/fileB.txt"); + assertNonPreviewResourceVersion(reader, "modfoo", "fileB.txt"); + assertDirContents(reader, "/modules/modfoo", "META-INF", "module-info.class", "fileA.txt", "fileB.txt", "com"); + + assertAbsent(reader, "/modules/modbaz/previewOnly.txt"); + assertDirContents(reader, "/modules/modbaz", "META-INF", "module-info.class", "file.txt", "normal.txt"); + assertNonPreviewResourceVersion(reader, "modbaz", "file.txt"); + assertNonPreviewResourceVersion(reader, "modbaz", "normal.txt"); } } @@ -289,6 +312,20 @@ public class ImageReaderTest { assertResource(reader, "modfoo", "com/foo/bar/IsPreviewOnly.class"); assertDirContents(reader, "/modules/modfoo/com/foo", "HasPreviewVersion.class", "NormalFoo.class", "bar"); assertDirContents(reader, "/modules/modfoo/com/foo/bar", "NormalBar.class", "IsPreviewOnly.class"); + + // Non-class resource in top level directory + assertResource(reader, "modfoo", "fileA.txt"); + assertPreviewResourceVersion(reader, "modfoo", "fileA.txt"); + assertNode(reader, "/modules/modfoo/fileB.txt"); + assertPreviewResourceVersion(reader, "modfoo", "fileB.txt"); + assertDirContents(reader, "/modules/modfoo/com", "foo"); + assertDirContents(reader, "/modules/modfoo", "META-INF", "module-info.class", "fileA.txt", "fileB.txt", "com"); + + assertNode(reader, "/modules/modbaz/previewOnly.txt"); + assertDirContents(reader, "/modules/modbaz", "META-INF", "module-info.class", "file.txt", "normal.txt", "previewOnly.txt"); + assertPreviewResourceVersion(reader, "modbaz", "file.txt"); + assertNonPreviewResourceVersion(reader, "modbaz", "normal.txt"); + assertPreviewResourceVersion(reader, "modbaz", "previewOnly.txt"); } } @@ -405,6 +442,17 @@ public class ImageReaderTest { assertSame(resNode, reader.findNode(nodeName)); } + private static void assertNonPreviewResourceVersion(ImageReader reader, String modName, String resPath) throws IOException { + Node resNode = reader.findResourceNode(modName, resPath); + assertArrayEquals(resPath.getBytes(StandardCharsets.UTF_8), reader.getResource(resNode)); + } + + private static void assertPreviewResourceVersion(ImageReader reader, String modName, String resPath) throws IOException { + Node resNode = reader.findResourceNode(modName, resPath); + String name = "META-INF/preview/" + resPath; + assertArrayEquals(name.getBytes(StandardCharsets.UTF_8), reader.getResource(resNode)); + } + private static void assertNonPreviewVersion(ImageClassLoader loader, String module, String fqn) throws IOException { assertEquals("Class: " + fqn, loader.loadAndGetToString(module, fqn)); } @@ -441,7 +489,7 @@ public class ImageReaderTest { classes.forEach(fqn -> { if (fqn.startsWith("!")) { - jar.addEntry(fqn.substring(1), "resource".getBytes(StandardCharsets.UTF_8)); + jar.addEntry(fqn.substring(1), fqn.substring(1).getBytes(StandardCharsets.UTF_8)); return; } boolean isPreviewEntry = fqn.startsWith("@"); diff --git a/test/jdk/jdk/jfr/api/consumer/TestRecordingFileWrite.java b/test/jdk/jdk/jfr/api/consumer/TestRecordingFileWrite.java index a804cc58a88..a27038c4abd 100644 --- a/test/jdk/jdk/jfr/api/consumer/TestRecordingFileWrite.java +++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingFileWrite.java @@ -64,6 +64,15 @@ public class TestRecordingFileWrite { throw new AssertionError("Expected at least 50 000 events to be included"); } verify(scrubbed, ids); + try { + scrubRecording(original, original); + throw new AssertionError("Expected IOException when overwriting"); + } catch (IOException e) { + System.out.println(e); + if (!e.getMessage().contains("Destination file can't be the same as the input file")) { + throw new AssertionError("Unexpected error message " + e); + } + } } private static void verify(Path scrubbed, Queue events) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java index d69d47f1911..cde561a68e7 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.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 @@ -107,9 +107,10 @@ public class TestG1ParallelPhases { "FreeCSet", "UpdateDerivedPointers", "EagerlyReclaimHumongousObjects", - "ResetPartialArrayStateManager", "ClearPendingCards", - "MergePSS", + "FlushPSS", + "DestroyPSS", + "UpdateCodeRoots", "NonYoungFreeCSet", "YoungFreeCSet", "RebuildFreeList", diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGarbageCollectionEventWithZMinor.java b/test/jdk/jdk/jfr/event/gc/collection/TestGarbageCollectionEventWithZMinor.java index 8e0d48682fc..da00274f5a6 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGarbageCollectionEventWithZMinor.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGarbageCollectionEventWithZMinor.java @@ -41,7 +41,6 @@ import jdk.test.whitebox.WhiteBox; * @test * @requires vm.flagless * @requires vm.hasJFR & vm.gc.Z - * @requires vm.flagless * @library /test/lib /test/jdk * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java b/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java index 559290f50b4..163644121db 100644 --- a/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java +++ b/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.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 @@ -30,10 +30,11 @@ import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.jfr.EventNames; import jdk.test.lib.jfr.Events; import jdk.test.lib.security.TestTLSHandshake; +import jdk.test.lib.security.SecurityUtils; /* * @test - * @bug 8148188 + * @bug 8148188 8301626 * @summary Enhance the security libraries to record events of interest * @requires vm.flagless * @requires vm.hasJFR @@ -41,28 +42,54 @@ import jdk.test.lib.security.TestTLSHandshake; * @run main/othervm jdk.jfr.event.security.TestTLSHandshakeEvent */ public class TestTLSHandshakeEvent { - public static void main(String[] args) throws Exception { - try (Recording recording = new Recording()) { - recording.enable(EventNames.TLSHandshake).withStackTrace(); - recording.start(); - TestTLSHandshake handshake = new TestTLSHandshake(); - handshake.run(); - recording.stop(); + record TLSConfig(String protocol, String cipherSuite, String namedGroup, + long certId) {}; - List events = Events.fromRecording(recording); - Events.hasEvents(events); - assertEvent(events, handshake); + private static final List CONFIGS = List.of( + new TLSConfig("TLSv1.3", "TLS_AES_256_GCM_SHA384", "X25519MLKEM768", + 3237675498L), + new TLSConfig("TLSv1.3", "TLS_CHACHA20_POLY1305_SHA256", + "X25519MLKEM768", 3237675498L), + new TLSConfig("TLSv1.2", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "secp256r1", 3237675498L), + new TLSConfig("TLSv1.2", "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", + "ffdhe2048", 3188175476L), + new TLSConfig("TLSv1.2", "TLS_RSA_WITH_AES_256_CBC_SHA", + "N/A", 3010289526L) + ); + + public static void main(String[] args) throws Exception { + // re-enable TLS_RSA suites as one test depends on it + SecurityUtils.removeFromDisabledTlsAlgs("TLS_RSA_*"); + for (TLSConfig config : CONFIGS) { + System.out.println(config); + try (Recording recording = new Recording()) { + recording.enable(EventNames.TLSHandshake).withStackTrace(); + recording.start(); + TestTLSHandshake handshake = new TestTLSHandshake(); + handshake.protocolVersion = config.protocol(); + handshake.cipherSuite = config.cipherSuite(); + handshake.namedGroup = config.namedGroup(); + handshake.run(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + assertEvent(events, handshake, config); + } } } - private static void assertEvent(List events, TestTLSHandshake handshake) throws Exception { + private static void assertEvent(List events, + TestTLSHandshake handshake, TLSConfig config) throws Exception { System.out.println(events); for (RecordedEvent e : events) { if (handshake.peerHost.equals(e.getString("peerHost"))) { Events.assertField(e, "peerPort").equal(handshake.peerPort); Events.assertField(e, "protocolVersion").equal(handshake.protocolVersion); - Events.assertField(e, "certificateId").equal(TestTLSHandshake.CERT_ID); - Events.assertField(e, "cipherSuite").equal(TestTLSHandshake.CIPHER_SUITE); + Events.assertField(e, "certificateId").equal(config.certId()); + Events.assertField(e, "cipherSuite").equal(handshake.cipherSuite); + Events.assertField(e, "namedGroup").equal(handshake.namedGroup); var method = e.getStackTrace().getFrames().get(0).getMethod(); if (method.getName().equals("recordEvent")) { throw new Exception("Didn't expected recordEvent as top frame"); diff --git a/test/jdk/jdk/jfr/jcmd/TestJcmdChangeLogLevel.java b/test/jdk/jdk/jfr/jcmd/TestJcmdChangeLogLevel.java index c4ef5ecfa74..a6dcd458383 100644 --- a/test/jdk/jdk/jfr/jcmd/TestJcmdChangeLogLevel.java +++ b/test/jdk/jdk/jfr/jcmd/TestJcmdChangeLogLevel.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 @@ -29,6 +29,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; +import java.util.regex.Pattern; import jdk.test.lib.dcmd.JcmdExecutor; import jdk.test.lib.dcmd.PidJcmdExecutor; @@ -46,7 +47,7 @@ import jdk.test.lib.dcmd.PidJcmdExecutor; public class TestJcmdChangeLogLevel { public static void main(String[] args) throws Exception { final String fileName = "jfr_trace.txt"; - final String findWhat = "[info][jfr] Flight Recorder initialized"; + final Pattern findWhat = Pattern.compile("\\[info *\\]\\[jfr *\\] Flight Recorder initialized"); boolean passed = false; JcmdExecutor je = new PidJcmdExecutor(); @@ -61,7 +62,7 @@ public class TestJcmdChangeLogLevel { throw new Error(e); } for (String l : lines) { - if (l.toString().contains(findWhat)) { + if (findWhat.matcher(l).find()) { passed = true; break; } diff --git a/test/jdk/jdk/jfr/jcmd/TestJcmdConfigure.java b/test/jdk/jdk/jfr/jcmd/TestJcmdConfigure.java index f64c99bedbc..8000199558c 100644 --- a/test/jdk/jdk/jfr/jcmd/TestJcmdConfigure.java +++ b/test/jdk/jdk/jfr/jcmd/TestJcmdConfigure.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 @@ -29,6 +29,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import jdk.jfr.internal.Repository; import jdk.jfr.internal.Options; @@ -127,7 +128,8 @@ public class TestJcmdConfigure { } private static void testRepository(){ - final String findWhat = "[info][jfr] Same base repository path " + REPOSITORYPATH_1 + " is set"; + final Pattern findWhat = Pattern.compile("\\[info *\\]\\[jfr *\\] Same base repository path " + + Pattern.quote(REPOSITORYPATH_1) + " is set"); try { JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_1); @@ -138,7 +140,7 @@ public class TestJcmdConfigure { Asserts.assertTrue(samePath.equals(initialPath)); List lines = Files.readAllLines(Paths.get(JFR_UNIFIED_LOG_FILE)); - Asserts.assertTrue(lines.stream().anyMatch(l->l.contains(findWhat))); + Asserts.assertTrue(lines.stream().anyMatch(l -> findWhat.matcher(l).find())); JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_2); Path changedPath = Repository.getRepository().getRepositoryPath(); diff --git a/test/jdk/jdk/jfr/startupargs/TestRedact.java b/test/jdk/jdk/jfr/startupargs/TestRedact.java index 2d96408a3f5..f3f3a9e5fa6 100644 --- a/test/jdk/jdk/jfr/startupargs/TestRedact.java +++ b/test/jdk/jdk/jfr/startupargs/TestRedact.java @@ -42,6 +42,7 @@ import jdk.jfr.consumer.EventStream; import jdk.jfr.consumer.RecordingFile; import jdk.test.lib.Asserts; import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.Platform; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; @@ -169,6 +170,7 @@ public class TestRedact { testRedactKey(); testRedactArgument(); testRedactMultiple(); + testOptionVariable(); testWildcards(); testDefaults(); testRedactFile(); @@ -315,15 +317,6 @@ public class TestRedact { private static void testEmpty() throws Exception { var environment = Map.of("API_TOKEN", "Zebra1"); var properties = Map.of("API_KEY", "Zebra2"); - Execution e1 = run(environment, properties, - "-XX:FlightRecorderOptions:redact-key=,redact-argument=", "Zebra3" - ); - e1.output().shouldContain("Default redaction filters are replaced."); - e1.output().shouldContain("redact-key=none to disable filters without a warning"); - e1.output().shouldContain("redact-argument=none to disable filters without a warning"); - e1.assertUnredacted("Zebra1"); - e1.assertUnredacted("Zebra2"); - e1.assertUnredacted("Zebra3"); Execution e2 = run(environment, properties, "-XX:FlightRecorderOptions:redact-argument=none,redact-key=none", "Zebra3" @@ -377,6 +370,12 @@ public class TestRedact { e.assertRedactedArgument("N4711"); e.assertRedactedArgument("Smith:abc123"); e.assertUnredacted("Banana"); + + String option = Platform.isWindows() ? + "-XX:FlightRecorderOptions:redact-argument='Foo,bar'" : + "-XX:FlightRecorderOptions:redact-argument=\"Foo,bar\""; + Execution e2 = run(option,"Foo,bar"); + e2.assertRedactedArgument("Foo,bar"); } private static void testRedactMultiple() throws Exception { @@ -390,6 +389,69 @@ public class TestRedact { e.assertRedactedArgument("Quz"); } + private static void testOptionVariable() throws Exception { + // Simulate shell expansion with the three options: + // SYSTEM_PROPS, JVM_OPTIONS and PROGRAM_OPTIONS + String systemProperty = "-Dsecret=apple"; + String jvmOption = "-XX:FlightRecorderOptions:stackdepth=32,redact-argument=+Aracuan"; + String programOption = "Aracuan"; + Execution e1 = run( + Map.of("SYSTEM_PROPS", systemProperty, + "JVM_OPTIONS", jvmOption, + "PROGRAM_OPTIONS", programOption), + Map.of("secret","apple"), + List.of(systemProperty, jvmOption), + programOption + ); + e1.assertRedactedKey("SYSTEM_PROPS"); + String redactedJVMOption = e1.environment.get("JVM_OPTIONS"); + if (!redactedJVMOption.equals("-XX:FlightRecorderOptions:stackdepth=32,redact-argument=[REDACTED]")) { + throw new Exception("Expected partial redaction for environment variable with -XX:FlightRecorderOptions:redact-argument="); + } + e1.assertRedactedKey("PROGRAM_OPTIONS"); + e1.assertRedactedKey("secret"); + e1.assertRedactedArgument("Aracuan"); + + Execution e2 = run( + Map.of("PROGRAM_OPTIONS", "BLUE RED GREEN GREDELINE"), + Map.of(), + "-XX:FlightRecorderOptions:redact-argument=+*red*", + "BLUE", "RED", "GREEN", "GREDELINE" + ); + String programOptions = e2.environment().get("PROGRAM_OPTIONS"); + if (!programOptions.equals("BLUE [REDACTED] GREEN [REDACTED]")) { + e2.print(); + throw new Exception("Missing redaction inside option variable"); + } + + Execution e3 = run( + Map.of("PROGRAM_OPTIONS", "ZEBRA FISH ZEBRACCOON FISH RACCOON"), + Map.of(), + "-XX:FlightRecorderOptions:redact-argument=+zebra;raccoon", + "ZEBRA", "FISH", "ZEBRACCOON", "FISH", "RACCOON" + ); + programOptions = e3.environment().get("PROGRAM_OPTIONS"); + if (!programOptions.equals("[REDACTED] FISH [REDACTED] FISH [REDACTED]")) { + e3.print(); + throw new Exception("Incorrect redaction when option arguments overlap"); + } + + String option1 = "-XX:FlightRecorderOptions:redact-argument=Zebra,gibberish=,,,"; + String option2 = "-XX:FlightRecorderOptions:redact-argument=Tiger"; + Execution e4 = run( + Map.of("MY_JVM_OPTIONS", option1 + " " + option2), + Map.of(), + List.of(option1, option2), + "TIGER" + ); + e4.assertRedactedArgument("TIGER"); + String redacted = e4.environment().get("MY_JVM_OPTIONS"); + if (!redacted.equals("[REDACTED] -XX:FlightRecorderOptions:redact-argument=[REDACTED]")) { + e4.print(); + throw new Exception("Incorrect redaction with multiple options in environment variables"); + } + } + private static void testRedactKey() throws Exception { Execution e = run( Map.of("cart", "wheel", "banana", "split", "rose", "bud"), @@ -407,14 +469,20 @@ public class TestRedact { return run(Map.of(), Map.of(), options, args); } - private static Execution run(Map environment, Map properties, String options, String... args) throws Exception { + private static Execution run(Map environment, Map properties, String option, String... args) throws Exception { + return run(environment, properties, List.of(option), args); + } + + private static Execution run(Map environment, Map properties, List options, String... args) throws Exception { List arguments = new ArrayList<>(); Path file = Path.of("file.jfr"); for (var entry : properties.entrySet()) { arguments.add("-D" + entry.getKey() + "=" + entry.getValue()); } arguments.add("-XX:StartFlightRecording:filename=" + file.toAbsolutePath().toString()); - arguments.add(options); + for (String option : options) { + arguments.add(option); + } arguments.add("jdk.jfr.startupargs.Application"); arguments.addAll(Arrays.asList(args)); diff --git a/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java b/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java index 4a934ebb36d..28e2891a08c 100644 --- a/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java +++ b/test/jdk/jdk/jfr/startupargs/TestStartupMessage.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 @@ -59,12 +59,12 @@ public class TestStartupMessage { // Can't turn off log with -Xlog:jfr+startup=warning startJfrJvm() - .shouldContain("[info][jfr,startup") + .shouldMatch("\\[info *\\]\\[jfr,startup") .shouldContain("Started recording") .shouldContain("Use jcmd"); startJfrJvm("-Xlog:jfr+startup=info") - .shouldContain("[info][jfr,startup") + .shouldMatch("\\[info *\\]\\[jfr,startup") .shouldContain("Started recording") .shouldContain("Use jcmd"); } diff --git a/test/jdk/jdk/security/logging/TestTLSHandshakeLog.java b/test/jdk/jdk/security/logging/TestTLSHandshakeLog.java index db34fb113d6..8986bf335a6 100644 --- a/test/jdk/jdk/security/logging/TestTLSHandshakeLog.java +++ b/test/jdk/jdk/security/logging/TestTLSHandshakeLog.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 @@ -27,13 +27,20 @@ import jdk.test.lib.security.TestTLSHandshake; /* * @test - * @bug 8148188 + * @bug 8148188 8301626 * @summary Enhance the security libraries to record events of interest * @library /test/lib /test/jdk * @run main/othervm jdk.security.logging.TestTLSHandshakeLog LOGGING_ENABLED * @run main/othervm jdk.security.logging.TestTLSHandshakeLog LOGGING_DISABLED */ public class TestTLSHandshakeLog { + record TLSConfig(String protocol, String cipherSuite, String namedGroup) {}; + + private static TLSConfig CONFIG = + new TLSConfig("TLSv1.2", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "secp256r1"); + public static void main(String[] args) throws Exception { LogJvm l = new LogJvm(TLSHandshake.class, args); l.addExpected("FINE: X509Certificate: Alg:SHA256withRSA, Serial:" + TestTLSHandshake.CERT_SERIAL); @@ -44,15 +51,20 @@ public class TestTLSHandshakeLog { ", " + TestTLSHandshake.CERT_ID); l.addExpected("SunJSSE Test Serivce"); l.addExpected("TLSHandshake:"); - l.addExpected("TLSv1.2"); - l.addExpected(TestTLSHandshake.CIPHER_SUITE +", " + TestTLSHandshake.CERT_ID); + l.addExpected(CONFIG.protocol()); + l.addExpected(CONFIG.cipherSuite()); + l.addExpected(CONFIG.namedGroup()); + l.addExpected(Long.toString(TestTLSHandshake.CERT_ID)); l.testExpected(); } public static class TLSHandshake { public static void main(String[] args) throws Exception { TestTLSHandshake handshake = new TestTLSHandshake(); + handshake.protocolVersion = CONFIG.protocol(); + handshake.cipherSuite = CONFIG.cipherSuite(); + handshake.namedGroup = CONFIG.namedGroup(); handshake.run(); } } -} \ No newline at end of file +} diff --git a/test/jdk/sun/net/www/protocol/file/DirPermissionDenied.java b/test/jdk/sun/net/www/protocol/file/DirPermissionDenied.java index 0b5f00bc7b9..6d7351cd497 100644 --- a/test/jdk/sun/net/www/protocol/file/DirPermissionDenied.java +++ b/test/jdk/sun/net/www/protocol/file/DirPermissionDenied.java @@ -23,24 +23,34 @@ /** * @test - * @bug 6977851 + * @bug 6977851 8385906 * @summary NPE from FileURLConnection.connect * @library /test/lib - * @build DirPermissionDenied jdk.test.lib.process.* - * jdk.test.lib.util.FileUtils + * @build DirPermissionDenied jdk.test.lib.util.FileUtils * @run junit ${test.main.class} */ import java.io.IOException; import java.net.URL; import java.net.URLConnection; +import java.nio.file.FileStore; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryPermission; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.FileUtils; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -51,6 +61,8 @@ public class DirPermissionDenied { private static final Path TEST_DIR = Paths.get( "DirPermissionDeniedDirectory"); private static URL url; + private static List aclEntries; + private static Set posixPermissions; @Test public void connectTest() throws IOException { @@ -73,21 +85,88 @@ public class DirPermissionDenied { @BeforeAll public static void setup() throws Throwable { url = new URL(TEST_DIR.toUri().toString()); - // mkdir and chmod "333" Files.createDirectories(TEST_DIR); - ProcessTools.executeCommand("chmod", "333", TEST_DIR.toString()) - .outputTo(System.out) - .errorTo(System.out) - .shouldHaveExitValue(0); + try { + makeDirectoryUnreadable(); + Assumptions.assumeTrue(TEST_DIR.toFile().list() == null, + "Could not make directory inaccessible"); + } catch (Throwable exception) { + restorePermissions(); + throw exception; + } } @AfterAll public static void tearDown() throws Throwable { - // add read permission to ensure the dir removable - ProcessTools.executeCommand("chmod", "733", TEST_DIR.toString()) - .outputTo(System.out) - .errorTo(System.out) - .shouldHaveExitValue(0); + restorePermissions(); FileUtils.deleteFileIfExistsWithRetry(TEST_DIR); } + + private static void makeDirectoryUnreadable() throws IOException { + FileStore store = Files.getFileStore(TEST_DIR); + if (store.supportsFileAttributeView("posix")) { + posixPermissions = Files.getPosixFilePermissions(TEST_DIR); + Set perms = EnumSet.of( + PosixFilePermission.OWNER_WRITE, + PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_WRITE, + PosixFilePermission.GROUP_EXECUTE); + Files.setPosixFilePermissions(TEST_DIR, perms); + } else if (store.supportsFileAttributeView("acl")) { + AclFileAttributeView view = Files.getFileAttributeView(TEST_DIR, + AclFileAttributeView.class); + if (view == null) { + throw new IOException("ACL view not available"); + } + aclEntries = new ArrayList<>(view.getAcl()); + List entries = new ArrayList<>(); + entries.add(AclEntry.newBuilder() + .setType(AclEntryType.DENY) + .setPrincipal(getCurrentUserPrincipal(view)) + .setPermissions(AclEntryPermission.READ_DATA, + AclEntryPermission.LIST_DIRECTORY) + .build()); + entries.addAll(aclEntries); + view.setAcl(entries); + } else { + Assumptions.assumeTrue(false, + "Required file attribute view not supported"); + } + } + + private static UserPrincipal getCurrentUserPrincipal( + AclFileAttributeView view) throws IOException { + String userName = System.getProperty("user.name"); + try { + return TEST_DIR.getFileSystem().getUserPrincipalLookupService() + .lookupPrincipalByName(userName); + } catch (IOException e) { + for (AclEntry entry : view.getAcl()) { + UserPrincipal principal = entry.principal(); + String name = principal.getName(); + if (name.equalsIgnoreCase(userName) + || name.endsWith("\\" + userName)) { + return principal; + } + } + throw e; + } + } + + private static void restorePermissions() throws IOException { + if (Files.notExists(TEST_DIR)) { + return; + } + + FileStore store = Files.getFileStore(TEST_DIR); + if (posixPermissions != null + && store.supportsFileAttributeView("posix")) { + Files.setPosixFilePermissions(TEST_DIR, posixPermissions); + } else if (aclEntries != null + && store.supportsFileAttributeView("acl")) { + AclFileAttributeView view = Files.getFileAttributeView(TEST_DIR, + AclFileAttributeView.class); + view.setAcl(aclEntries); + } + } } diff --git a/test/jdk/sun/nio/cs/TestStringCodingUTF8.java b/test/jdk/sun/nio/cs/TestStringCodingUTF8.java index 50d68f408d5..5b084013528 100644 --- a/test/jdk/sun/nio/cs/TestStringCodingUTF8.java +++ b/test/jdk/sun/nio/cs/TestStringCodingUTF8.java @@ -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 @@ -21,18 +21,26 @@ * questions. */ -/* @test - @bug 7040220 8054307 - @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 - * @run main/othervm/timeout=2000 TestStringCodingUTF8 +/* + * @test + * @bug 7040220 8054307 + * @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run main/othervm/timeout=2000 TestStringCodingUTF8 */ import java.util.*; import java.nio.*; import java.nio.charset.*; +import jdk.test.lib.RandomFactory; + public class TestStringCodingUTF8 { + + private static final Random rnd = RandomFactory.getRandom(); + public static void main(String[] args) throws Throwable { test("UTF-8"); test("CESU-8"); @@ -62,7 +70,7 @@ public class TestStringCodingUTF8 { for (int i = 0; i < 0x20000; i++) { list.add(i, i); } - Collections.shuffle(list); + Collections.shuffle(list, rnd); int j = 0; char[] bmpsupp = new char[0x30000]; for (int i = 0; i < 0x20000; i++) { @@ -72,7 +80,6 @@ public class TestStringCodingUTF8 { test(cs, bmpsupp, 0, bmpsupp.length); // randomed "off" and "len" on shuffled data - Random rnd = new Random(); int maxlen = 1000; int itr = 5000; for (int i = 0; i < itr; i++) { @@ -88,11 +95,13 @@ public class TestStringCodingUTF8 { //new String(csn); if (!new String(ba, cs.name()).equals( new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(csn) failed"); + throw new RuntimeException("new String(csn) failed for charset " + cs + + " for byte array of length " + ba.length); //new String(cs); if (!new String(ba, cs).equals( new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(cs) failed"); + throw new RuntimeException("new String(cs) failed for charset " + cs + + " for byte array of length " + ba.length); } System.out.println("done!"); } @@ -103,21 +112,23 @@ public class TestStringCodingUTF8 { //getBytes(csn); byte[] baStr = str.getBytes(cs.name()); - if (!Arrays.equals(ba, baStr)) - throw new RuntimeException("getBytes(csn) failed"); + failIfMismatch(ba, baStr, "getBytes(csn) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); //getBytes(cs); baStr = str.getBytes(cs); - if (!Arrays.equals(ba, baStr)) - throw new RuntimeException("getBytes(cs) failed"); + failIfMismatch(ba, baStr, "getBytes(cs) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); //new String(csn); if (!new String(ba, cs.name()).equals(new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(csn) failed"); + throw new RuntimeException("new String(csn) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); //new String(cs); if (!new String(ba, cs).equals(new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(cs) failed"); + throw new RuntimeException("new String(cs) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); } // copy/paste of the StringCoding.decode() @@ -170,4 +181,45 @@ public class TestStringCodingUTF8 { } return Arrays.copyOf(ba, bb.position()); } + + private static void failIfMismatch(final byte[] expected, final byte[] actual, + final String failureMsg) { + final int firstMismatchIndex = Arrays.mismatch(expected, actual); + if (firstMismatchIndex == -1) { + // no mismatch + return; + } + System.err.println("Arrays mismatch starts at index: " + firstMismatchIndex); + System.err.println("Printing few indexes before and after the mismatch:"); + int printStartIdx = firstMismatchIndex - 20; + if (printStartIdx < 0) { + printStartIdx = 0; + } + for (int i = printStartIdx; i < firstMismatchIndex + 20; i++) { + if (i >= expected.length && i >= actual.length) { + // no more elements in either arrays, we are done + break; + } + final StringBuilder sb = new StringBuilder(); + sb.append("Index=").append(i).append(", expected="); + if (i >= expected.length) { + // "expected" array isn't that big + sb.append(""); + } else { + sb.append(expected[i]); + } + sb.append(", actual="); + if (i >= actual.length) { + // "actual" array isn't that big + sb.append(""); + } else { + sb.append(actual[i]); + } + if (i == firstMismatchIndex) { + sb.append(" <--- first mismatch"); + } + System.err.println(sb.toString()); + } + throw new RuntimeException(failureMsg); + } } diff --git a/test/jdk/sun/security/internal/CheckIBE.java b/test/jdk/sun/security/internal/CheckIBE.java new file mode 100644 index 00000000000..802cd336ebc --- /dev/null +++ b/test/jdk/sun/security/internal/CheckIBE.java @@ -0,0 +1,67 @@ +/* + * 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 8383608 + * @summary check that InternalBinaryEncodable exists + * @enablePreview + * @modules java.base/sun.security.internal + * @run main CheckIBE + */ + +import javax.crypto.EncryptedPrivateKeyInfo; +import java.security.AsymmetricKey; +import java.security.BinaryEncodable; +import java.security.KeyPair; +import java.security.PEM; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import sun.security.internal.InternalBinaryEncodable; + +/* + * This test verifies that BinaryEncodable has the expected set of permitted + * subtypes, including InternalBinaryEncodable. If this switch stops compiling, + * update the cases to match the BinaryEncodable permits list. + */ + +public class CheckIBE { + public static void main(String[] args) { + BinaryEncodable be = new PEM("TEST", "TEST"); + + switch (be) { + case AsymmetricKey ignored -> {} + case KeyPair ignored -> {} + case PKCS8EncodedKeySpec ignored -> {} + case X509EncodedKeySpec ignored -> {} + case EncryptedPrivateKeyInfo ignored -> {} + case X509Certificate ignored -> {} + case X509CRL ignored -> {} + case PEM ignored -> {} + case InternalBinaryEncodable ignored -> {} + } + } +} diff --git a/test/jdk/sun/security/internal/ExhaustiveBE.java b/test/jdk/sun/security/internal/ExhaustiveBE.java new file mode 100644 index 00000000000..37222a0310a --- /dev/null +++ b/test/jdk/sun/security/internal/ExhaustiveBE.java @@ -0,0 +1,65 @@ +/* + * 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 8383608 + * @summary verify switches over BinaryEncodable are not exhaustive + * @enablePreview + * @compile/fail ExhaustiveBE.java + */ + +import javax.crypto.EncryptedPrivateKeyInfo; +import java.security.AsymmetricKey; +import java.security.BinaryEncodable; +import java.security.KeyPair; +import java.security.PEM; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/* + * This test verifies that application code cannot exhaustively switch over + * BinaryEncodable by naming only the public permitted subtypes. Compilation + * must fail because application code needs a default case, or a + * BinaryEncodable case, to cover the internal permitted subtype + * InternalBinaryEncodable. + */ + +public class ExhaustiveBE { + public static void main(String[] args) { + BinaryEncodable be = new PEM("TEST", "TEST"); + + switch (be) { + case AsymmetricKey ignored -> {} + case KeyPair ignored -> {} + case PKCS8EncodedKeySpec ignored -> {} + case X509EncodedKeySpec ignored -> {} + case EncryptedPrivateKeyInfo ignored -> {} + case X509Certificate ignored -> {} + case X509CRL ignored -> {} + case PEM ignored -> {} + } + } +} diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index c2c58b36c38..82b6a6c257e 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -28,7 +28,7 @@ * 8223499 8225392 8232019 8234245 8233223 8225068 8225069 8243321 8243320 * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 - * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 + * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 8387123 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -47,12 +47,12 @@ public class VerifyCACerts { + File.separator + "security" + File.separator + "cacerts"; // The numbers of certs now. - private static final int COUNT = 111; + private static final int COUNT = 110; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 private static final String CHECKSUM - = "26:75:A0:AA:6E:7C:15:8B:BC:CF:11:81:38:3E:E7:94:31:9E:36:2D:F9:A6:BC:88:E1:A5:F8:46:9A:4C:1D:D7"; + = "AA:C2:64:41:28:06:1F:83:92:54:7C:DD:95:82:61:4C:8F:FA:09:7B:17:64:A7:A8:7C:A9:F6:27:25:95:2D:BB"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -143,8 +143,6 @@ public class VerifyCACerts { "96:BC:EC:06:26:49:76:F3:74:60:77:9A:CF:28:C5:A7:CF:E8:A3:C0:AA:E1:1A:8F:FC:EE:05:C0:BD:DF:08:C6"); put("letsencryptisrgx2 [jdk]", "69:72:9B:8E:15:A8:6E:FC:17:7A:57:AF:B7:17:1D:FC:64:AD:D2:8C:2F:CA:8C:F1:50:7E:34:45:3C:CB:14:70"); - put("luxtrustglobalrootca [jdk]", - "A1:B2:DB:EB:64:E7:06:C6:16:9E:3C:41:18:B2:3B:AA:09:01:8A:84:27:66:6D:8B:F0:E2:88:91:EC:05:19:50"); put("quovadisrootca [jdk]", "A4:5E:DE:3B:BB:F0:9C:8A:E1:5C:72:EF:C0:72:68:D6:93:A2:1C:99:6F:D5:1E:67:CA:07:94:60:FD:6D:88:73"); put("quovadisrootca1g3 [jdk]", @@ -296,8 +294,6 @@ public class VerifyCACerts { add("addtrustexternalca [jdk]"); // Valid until: Sat May 30 10:44:50 GMT 2020 add("addtrustqualifiedca [jdk]"); - // Valid until: Wed Mar 17 02:51:37 PDT 2021 - add("luxtrustglobalrootca [jdk]"); // Valid until: Wed Mar 17 11:33:33 PDT 2021 add("quovadisrootca [jdk]"); // Valid until: Sat May 21 04:00:00 GMT 2022 diff --git a/test/jdk/sun/security/provider/acvp/LMS_Test.java b/test/jdk/sun/security/provider/acvp/LMS_Test.java index 692e398359f..c8858ff8d14 100644 --- a/test/jdk/sun/security/provider/acvp/LMS_Test.java +++ b/test/jdk/sun/security/provider/acvp/LMS_Test.java @@ -22,7 +22,7 @@ */ import jdk.test.lib.Asserts; import jdk.test.lib.json.JSONValue; -import sun.security.util.RawKeySpec; +import sun.security.util.KeyUtil; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -62,11 +62,11 @@ public class LMS_Test { // Convert to HSS key by prepending height of tree (1) // to the LMS public key. - RawKeySpec rks = new RawKeySpec(toByteArray( - "00000001" + t.get("publicKey").asString())); KeyFactory kf = p == null ? KeyFactory.getInstance("HSS/LMS") : KeyFactory.getInstance("HSS/LMS", p); - PublicKey pk1 = kf.generatePublic(rks); + PublicKey pk1 = (PublicKey) kf.translateKey(KeyUtil + .newRawPublicKey("HSS/LMS", toByteArray( + "00000001" + t.get("publicKey").asString()))); try { s.initVerify(pk1); diff --git a/test/jdk/sun/security/provider/hss/TestHSS.java b/test/jdk/sun/security/provider/hss/TestHSS.java index 48019d4e465..d2d815b2025 100644 --- a/test/jdk/sun/security/provider/hss/TestHSS.java +++ b/test/jdk/sun/security/provider/hss/TestHSS.java @@ -167,9 +167,9 @@ public class TestHSS { PublicKey pk1; // build public key - RawKeySpec rks = new RawKeySpec(pk); KeyFactory kf = KeyFactory.getInstance(ALG, provider); - pk1 = kf.generatePublic(rks); + pk1 = (PublicKey) kf.translateKey(KeyUtil + .newRawPublicKey(ALG, pk)); var v = Signature.getInstance(ALG); v.initVerify(pk1); diff --git a/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java b/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java index e58809fcb69..ff5430e60bc 100644 --- a/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java +++ b/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java @@ -36,7 +36,6 @@ import jdk.test.lib.security.SeededSecureRandom; import sun.security.pkcs.NamedPKCS8Key; import sun.security.provider.NamedKeyFactory; import sun.security.provider.NamedKeyPairGenerator; -import sun.security.util.RawKeySpec; import sun.security.x509.NamedX509Key; import java.security.*; @@ -119,30 +118,22 @@ public class NamedKeyFactoryTest { Utils.runAndCheckException(() -> kf5.generatePublic(skSpec), InvalidKeySpecException.class); - // The private RawKeySpec and unnamed RAW EncodedKeySpec - var prk = kf.getKeySpec(pk, RawKeySpec.class); - Asserts.assertEqualsByteArray(prk.getKeyArr(), pk.getRawBytes()); - var prk2 = kf.getKeySpec(pk, EncodedKeySpec.class); - Asserts.assertEquals("RAW", prk2.getFormat()); - Asserts.assertEqualsByteArray(prk.getKeyArr(), prk2.getEncoded()); + // The unnamed RAW EncodedKeySpec + var prk = kf.getKeySpec(pk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", prk.getFormat()); + Asserts.assertEqualsByteArray(pk.getRawBytes(), prk.getEncoded()); Asserts.assertEqualsByteArray(kf2.generatePublic(prk).getEncoded(), pk.getEncoded()); Utils.runAndCheckException(() -> kf.generatePublic(prk), InvalidKeySpecException.class); // no pname - Asserts.assertEqualsByteArray(kf2.generatePublic(prk2).getEncoded(), pk.getEncoded()); - Utils.runAndCheckException(() -> kf.generatePublic(prk2), InvalidKeySpecException.class); // no pname - var srk = kf.getKeySpec(sk, RawKeySpec.class); - Asserts.assertEqualsByteArray(srk.getKeyArr(), sk.getRawBytes()); - var srk2 = kf.getKeySpec(sk, EncodedKeySpec.class); - Asserts.assertEquals("RAW", srk2.getFormat()); - Asserts.assertEqualsByteArray(srk2.getEncoded(), sk.getRawBytes()); + var srk = kf.getKeySpec(sk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", srk.getFormat()); + Asserts.assertEqualsByteArray(srk.getEncoded(), sk.getRawBytes()); checkKey(kf2.generatePrivate(srk), "SHA", "SHA-256"); Asserts.assertEqualsByteArray(kf2.generatePrivate(srk).getEncoded(), sk.getEncoded()); Utils.runAndCheckException(() -> kf.generatePrivate(srk), InvalidKeySpecException.class); // no pname checkKey(kf2.generatePrivate(srk), "SHA", "SHA-256"); - Asserts.assertEqualsByteArray(kf2.generatePrivate(srk2).getEncoded(), sk.getEncoded()); - Utils.runAndCheckException(() -> kf.generatePrivate(srk2), InvalidKeySpecException.class); // no pname var pk1 = new PublicKey() { public String getAlgorithm() { return "SHA"; } diff --git a/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java b/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java index 42e9701c6d0..be0f0154e32 100644 --- a/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java +++ b/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java @@ -52,7 +52,7 @@ import jdk.test.lib.security.CertificateBuilder; * java.base/sun.security.util * @library /javax/net/ssl/templates * /test/lib - * @run main/othervm CompressedCertMsgCache + * @run main/othervm -Djdk.tls.server.newSessionTicketCount=0 CompressedCertMsgCache */ public class CompressedCertMsgCache extends SSLSocketTemplate { diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java new file mode 100644 index 00000000000..87a6f156152 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, IBM Corporation. All rights reserved. + * 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 8387124 + * @summary Test disabling TLS 1.3 cipher suites with bulk ciphers names + * @run testng/othervm TLS13BulkCipherDisabledCipherSuite + */ + +import static org.testng.AssertJUnit.assertTrue; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; +import java.util.List; + +public class TLS13BulkCipherDisabledCipherSuite extends AbstractDisableCipherSuites { + + private static final String SECURITY_PROPERTY = "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = "AES_256_GCM," + + " AES_128_GCM," + + " CHACHA20_POLY1305"; + private static final String[] CIPHER_SUITES = new String[] { + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_GCM_SHA256", + "TLS_CHACHA20_POLY1305_SHA256" + }; + static final List CIPHER_SUITES_IDS = List.of( + 0x1301, + 0x1302, + 0x1303); + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testDefault() throws Exception { + assertTrue(testDefaultCase(CIPHER_SUITES_IDS)); + } + + @Test + public void testAddDisabled() throws Exception { + assertTrue(testEngAddDisabled(CIPHER_SUITES, CIPHER_SUITES_IDS)); + } + + @Test + public void testOnlyDisabled() throws Exception { + assertTrue(testEngOnlyDisabled(CIPHER_SUITES)); + } +} diff --git a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java index d1918aab7f1..87c032728dc 100644 --- a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java +++ b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.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 @@ -93,6 +93,9 @@ public class ResumeChecksServer extends SSLContextTemplate { System.err.println("firstSession.getCreationTime() = " + firstSession.getCreationTime()); + // Sleep 100ms between 2 connections to avoid test flakiness. + Thread.sleep(100); + long secondStartTime = System.currentTimeMillis(); secondSession = c.test(); @@ -128,7 +131,8 @@ public class ResumeChecksServer extends SSLContextTemplate { case SIGNATURE_SCHEME: case LOCAL_CERTS: // fail if a new session is not created - if (secondSession.getCreationTime() < secondStartTime) { + if (secondSession.getCreationTime() == + firstSession.getCreationTime()) { throw new AssertionError("Existing session was used: FAIL"); } System.out.println("secondSession not resumed: PASS"); diff --git a/test/jdk/sun/tools/jps/TestJpsTempDir.java b/test/jdk/sun/tools/jps/TestJpsTempDir.java new file mode 100644 index 00000000000..b54fedad2b8 --- /dev/null +++ b/test/jdk/sun/tools/jps/TestJpsTempDir.java @@ -0,0 +1,72 @@ +/* + * 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 8384557 + * @summary Test to make sure jps works correctly when -XX:AltTempDir is set. + * @library /test/lib + * @requires os.family == "linux" + * @modules jdk.jartool/sun.tools.jar + * @build jdk.test.lib.apps.LingeredApp + * @run main/othervm TestJpsTempDir + */ + +// Test that jps finds hsperfdata file in -XX:AltTempDir. + +import jdk.test.lib.apps.LingeredApp; +import java.util.ArrayList; +import java.util.List; +import java.nio.file.Path; +import java.nio.file.Files; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.FileUtils; + +public class TestJpsTempDir { + + public static void main(java.lang.String[] unused) throws Exception { + Path clientTmpDir = Files.createTempDirectory(Path.of("/tmp"), "c"); + String tmpdirString = "-XX:AltTempDir=" + clientTmpDir.toString(); + + LingeredAppForJps app = new LingeredAppForJps(); + + try { + // Start LingeredApp with AltTempDir + List vmArgs = new ArrayList<>(List.of(JpsHelper.getVmArgs())); + vmArgs.add(tmpdirString); + LingeredApp.startApp(app, vmArgs.toArray(String[]::new)); + + // Pass to jps (adds -J) + List jpsArgs = new ArrayList<>(); + jpsArgs.add(tmpdirString); + + OutputAnalyzer output = JpsHelper.jps(jpsArgs, null); + output.shouldContain(app.getProcessName()); + output.shouldContain(Long.toString(app.getPid())); + output.shouldHaveExitValue(0); + } finally { + LingeredApp.stopApp(app); + FileUtils.deleteFileTreeWithRetry(clientTmpDir); + } + } +} diff --git a/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh index c1908855ea7..6e184e9dc31 100644 --- a/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcCapacityOutput1.sh # @summary Test that output of 'jstat -gccapacity 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh index b16d0e38d02..b5caccb1768 100644 --- a/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcMetaCapacityOutput1.sh # @summary Test that output of 'jstat -gcmetacapacity 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh index 64ce2efd455..96f0722a9e1 100644 --- a/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcNewCapacityOutput1.sh # @summary Test that output of 'jstat -gcnewcapacity 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh index b15ec02d2b0..96e2db61488 100644 --- a/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcNewOutput1.sh # @summary Test that output of 'jstat -gcnew 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh index 1c13d6f916d..0c5e2e19894 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh @@ -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 @@ -23,8 +23,9 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcOldCapacityOutput1.sh -# @summary Test that output of 'jstat -gcoldcapcaity 0' has expected line counts +# @summary Test that output of 'jstat -gcoldcapacity 0' has expected line counts . ${TESTSRC-.}/../../jvmstat/testlibrary/utils.sh diff --git a/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh index 7f505228b12..0f857ccb1e6 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcOldOutput1.sh # @summary Test that output of 'jstat -gcold 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOutput1.sh index dfffa2d1a55..5862fda3fd7 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOutput1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcOutput1.sh # @summary Test that output of 'jstat -gc 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts1.sh b/test/jdk/sun/tools/jstat/jstatLineCounts1.sh index 97338b8e793..ca6adce96a5 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts1.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts1.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts1.sh # @summary Test that output of 'jstat -gcutil 0 250 5' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts2.sh b/test/jdk/sun/tools/jstat/jstatLineCounts2.sh index eab19f3931e..a668df72e0e 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts2.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts2.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts2.sh # @summary Test that output of 'jstat -gcutil 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts3.sh b/test/jdk/sun/tools/jstat/jstatLineCounts3.sh index 9a769a92464..bffffc8a38e 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts3.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts3.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts3.sh # @summary Test that output of 'jstat -gcutil -h 10 250 10' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts4.sh b/test/jdk/sun/tools/jstat/jstatLineCounts4.sh index 817c3b14f62..9ad1f57a5d5 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts4.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts4.sh @@ -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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts4.sh # @summary Test that output of 'jstat -gcutil -h 10 250 11' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh b/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh index db71314fcf9..4e4cb8df426 100644 --- a/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh +++ b/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatTimeStamp1.sh # @summary Test that output of 'jstat -gcutil -t 0' has expected format diff --git a/test/jdk/tools/jimage/JImageExtractTest.java b/test/jdk/tools/jimage/JImageExtractTest.java index eec10439cf5..e5f62232097 100644 --- a/test/jdk/tools/jimage/JImageExtractTest.java +++ b/test/jdk/tools/jimage/JImageExtractTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, 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 @@ -188,7 +188,8 @@ public class JImageExtractTest extends JImageCliTest { AclEntry entry = AclEntry.newBuilder() .setType(AclEntryType.DENY) .setPrincipal(fileOwner) - .setPermissions(AclEntryPermission.WRITE_DATA) + .setPermissions(AclEntryPermission.WRITE_DATA, + AclEntryPermission.APPEND_DATA) .setFlags(AclEntryFlag.FILE_INHERIT, AclEntryFlag.DIRECTORY_INHERIT) .build(); List acl = view.getAcl(); diff --git a/test/jdk/tools/jlink/plugins/CACertsPluginTest.java b/test/jdk/tools/jlink/plugins/CACertsPluginTest.java new file mode 100644 index 00000000000..8ca720be639 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/CACertsPluginTest.java @@ -0,0 +1,120 @@ +/* + * 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.nio.file.Path; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.Enumeration; + +import jtreg.SkippedException; +import jdk.test.lib.Asserts; +import jdk.test.lib.security.SecurityUtils; +import jdk.tools.jlink.internal.LinkableRuntimeImage; +import tests.Helper; + +/* @test + * @bug 8377102 + * @summary Test the --cacerts plugin + * @library ../../lib /test/lib + * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jimage + * jdk.jlink/jdk.tools.jlink.internal + * @build tests.* + * @run main/othervm CACertsPluginTest + */ + +public class CACertsPluginTest { + + private static Helper helper; + + private static final String CACERTS_PATH = "lib/security/cacerts"; + private static final boolean LINKABLE_RUNTIME = + LinkableRuntimeImage.isLinkableRuntime(); + + public static void main(String[] args) throws Throwable { + + helper = Helper.newHelper(LINKABLE_RUNTIME); + if (helper == null) { + throw new SkippedException("Test not run"); + } + + KeyStore jdkCacerts = SecurityUtils.getCacertsKeyStore(); + Enumeration aliases = jdkCacerts.aliases(); + String alias1 = aliases.nextElement(); + String alias2 = aliases.nextElement(); + + // test one alias + test("testOne", jdkCacerts, alias1); + + // test two aliases + test("testTwo", jdkCacerts, alias1, alias2); + + // test illegal/bad options + testBadOptions(); + } + + private static void test(String module, KeyStore jdkCacerts, + String... aliases) throws Exception { + + helper.generateDefaultJModule(module); + + String option = toOption(aliases); + Path image = helper.generateDefaultImage( + new String[] { "--cacerts", option }, module).assertSuccess(); + helper.checkImage(image, module, null, null, + new String[] { CACERTS_PATH }); + + KeyStore imageCacerts = KeyStore.getInstance( + image.resolve(CACERTS_PATH).toFile(), (char[]) null); + + Asserts.assertEquals(imageCacerts.size(), aliases.length); + for (String alias : aliases) { + Asserts.assertTrue(imageCacerts.isCertificateEntry(alias)); + Asserts.assertEquals( + jdkCacerts.getCertificate(alias), + imageCacerts.getCertificate(alias)); + } + } + + private static void testBadOptions() throws Exception { + + String module = "testBad"; + helper.generateDefaultJModule(module); + helper.generateDefaultImage(new String[] + { "--cacerts", "bogus-alias" }, module) + .assertFailure("alias bogus-alias does not exist"); + } + + private static String toOption(String... aliases) { + int max = aliases.length - 1; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; ; i++) { + sb.append(aliases[i]); + if (i == max) { + return sb.toString(); + } + sb.append(","); + } + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index 639bd295e57..2dc2351be3c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -27,7 +27,9 @@ import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; @@ -35,11 +37,13 @@ import java.time.Instant; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -259,9 +263,19 @@ public class WindowsHelper { } public static WixType getWixTypeFromVerboseJPackageOutput(Executor.Result result) { + return getWixTypeFromVerboseJPackageOutput(Locale.getDefault(), result); + } - var summaryWixVersion = JPackageStringBundle.MAIN.cannedFormattedString( - "summary.property.win-wix-version").getValue() + ": "; + public static WixType getWixTypeFromVerboseJPackageOutput(Locale resultLocale, Executor.Result result) { + + final String summaryWixVersion; + if (resultLocale.equals(Locale.getDefault())) { + summaryWixVersion = JPackageStringBundle.MAIN.cannedFormattedString( + "summary.property.win-wix-version").getValue() + ": "; + } else { + summaryWixVersion = JPackageResourceBundleCache.INSTANCE.get(resultLocale).getString( + "summary.property.win-wix-version") + ": "; + } return result.stdout().stream().filter(str -> { return str.startsWith(summaryWixVersion); @@ -646,6 +660,26 @@ public class WindowsHelper { } + private static final class JPackageResourceBundleCache { + + ResourceBundle get(Locale locale) { + synchronized (items) { + var value = Optional.ofNullable(items.get(locale)).map(Reference::get).orElse(null); + if (value == null) { + value = ResourceBundle.getBundle("jdk.jpackage.internal.resources.WinResources", + locale, ModuleLayer.boot().findModule("jdk.jpackage").orElseThrow()); + items.put(locale, new WeakReference<>(value)); + } + return value; + } + } + + private final Map> items = new HashMap<>(); + + static final JPackageResourceBundleCache INSTANCE = new JPackageResourceBundleCache(); + } + + static final Set CRITICAL_RUNTIME_FILES = Set.of(Path.of( "bin\\server\\jvm.dll")); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java index 4f67909bd47..a64d642f633 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java @@ -890,15 +890,18 @@ public class CommandOutputControlTest { for (boolean toolProvider : BOOLEAN_VALUES) { for (var redirectStderr : withAndWithout(OutputControl.REDIRECT_STDERR)) { - for (var charset : withAndWithout(OutputControl.CHARSET_UTF16LE)) { - var stdoutSink = new CharsetTestSpec.DumpOutputSink(StandardCharsets.US_ASCII, OutputStreams.STDOUT); - var stderrSink = new CharsetTestSpec.DumpOutputSink(StandardCharsets.UTF_32LE, OutputStreams.STDERR); - var outputControl = new HashSet(); - redirectStderr.ifPresent(outputControl::add); - charset.ifPresent(outputControl::add); - outputControl.add(stdoutSink); - outputControl.add(stderrSink); - testCases.add(new CharsetTestSpec(toolProvider, new CommandOutputControlSpec(outputControl))); + for (var saveOutput : withAndWithout(OutputControl.SAVE_ALL)) { + for (var charset : withAndWithout(OutputControl.CHARSET_UTF16LE)) { + var stdoutSink = new CharsetTestSpec.DumpOutputSink(StandardCharsets.US_ASCII, OutputStreams.STDOUT); + var stderrSink = new CharsetTestSpec.DumpOutputSink(StandardCharsets.UTF_32LE, OutputStreams.STDERR); + var outputControl = new HashSet(); + redirectStderr.ifPresent(outputControl::add); + saveOutput.ifPresent(outputControl::add); + charset.ifPresent(outputControl::add); + outputControl.add(stdoutSink); + outputControl.add(stderrSink); + testCases.add(new CharsetTestSpec(toolProvider, new CommandOutputControlSpec(outputControl))); + } } } } @@ -1731,37 +1734,46 @@ public class CommandOutputControlTest { record CharsetTestSpec(boolean toolProvider, CommandOutputControlSpec cocSpec) { - void test() throws IOException, InterruptedException { - if (cocSpec.outputControl().stream().noneMatch(DumpOutputSink.class::isInstance)) { + CharsetTestSpec(boolean toolProvider, CommandOutputControlSpec cocSpec) { + this.toolProvider = toolProvider; + this.cocSpec = Objects.requireNonNull(cocSpec); + + // Sinks must be specified for stdout and stderr streams. + if (cocSpec.outputControl().stream().filter(DumpOutputSink.class::isInstance).count() != 2) { throw new IllegalArgumentException(); } + } - final var expectedString = "veni-vidi-vici"; + void test() throws IOException, InterruptedException { + + final var writeToStdout = "veni-vidi-vici"; + final var writeToStderr = "iciv-idiv-inev"; var coc = cocSpec.create().dumpOutput(true); CommandOutputControl.Executable exec; if (toolProvider) { - var tp = Command.createToolProvider(Stream.of(expectedString).mapMulti((str, sink) -> { - sink.accept(CommandAction.echoStdout(str)); - sink.accept(CommandAction.echoStderr(str)); - }).toList()); + var tp = Command.createToolProvider(List.of( + CommandAction.echoStdout(writeToStdout), + CommandAction.echoStderr(writeToStderr) + )); exec = coc.createExecutable(tp); } else { - var cmdline = Command.createShellCommandLine(Stream.of(expectedString).map(str -> { + Function conv = str -> { return (str + System.lineSeparator()).getBytes(coc.charset()); - }).mapMulti((bytes, sink) -> { - sink.accept(CommandAction.writeStdout(bytes)); - sink.accept(CommandAction.writeStderr(bytes)); - }).toList()); + }; + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.writeStdout(conv.apply(writeToStdout)), + CommandAction.writeStderr(conv.apply(writeToStderr)) + )); exec = coc.createExecutable(new ProcessBuilder(cmdline)); } - exec.execute(); + final var execResult = exec.execute(); for (var outputContolMutator : cocSpec.outputControl()) { if (outputContolMutator instanceof DumpOutputSink sink) { - var actual = sink.lines(); + var actual = sink.lines(coc); List expected; if (cocSpec.redirectStderr()) { switch (sink.streams()) { @@ -1769,13 +1781,22 @@ public class CommandOutputControlTest { expected = List.of(); } default -> { - expected = List.of(expectedString, expectedString); + expected = List.of(writeToStdout, writeToStderr); } } } else { - expected = List.of(expectedString); + switch (sink.streams()) { + case STDERR -> { + expected = List.of(writeToStderr); + } + default -> { + expected = List.of(writeToStdout); + } + } } assertEquals(expected, actual); + } else if (outputContolMutator == OutputControl.SAVE_ALL) { + assertEquals(List.of(writeToStdout, writeToStderr), execResult.content()); } } @@ -1792,8 +1813,8 @@ public class CommandOutputControlTest { this(charset, new ByteArrayOutputStream(), streams); } - List lines() { - var str = buffer.toString(charset); + List lines(CommandOutputControl coc) { + var str = buffer.toString((coc.isSaveOutput() || coc.isSaveFirstLineOfOutput()) ? coc.charset() : charset); return new BufferedReader(new StringReader(str)).lines().toList(); } diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java new file mode 100644 index 00000000000..0943f6c9733 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java @@ -0,0 +1,82 @@ +/* + * 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.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + + +class RtfConverterTest { + + @Test + void test_createSimple_dir(@TempDir Path workDir) throws IOException { + + assertEquals(Optional.empty(), RtfConverter.createSimple(workDir)); + } + + @ParameterizedTest + @ValueSource(strings = { + // Empty value to exercise the case when the file's content is shorter than the RTF's header + "", + // Value to exercise the case when the file's content is shorter than the RTF's header + "Hello", + "Hello Duke!", + }) + void test_createSimple_text_file(String text, @TempDir Path workDir) throws IOException { + + final var licenseFile = workDir.resolve("license"); + + Files.writeString(licenseFile, text); + + final var conv = RtfConverter.createSimple(licenseFile); + + assertTrue(conv.isPresent()); + + conv.orElseThrow().convert(licenseFile); + + assertEquals(Optional.empty(), RtfConverter.createSimple(licenseFile)); + } + + @ParameterizedTest + @ValueSource(strings = { + "{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0 Arial;}}\\f0\\fs24 Hello, Duke!}", + }) + void test_createSimple_rtf_file(String text, @TempDir Path workDir) throws IOException { + + final var licenseFile = workDir.resolve("license"); + + Files.writeString(licenseFile, text); + + assertEquals(Optional.empty(), RtfConverter.createSimple(workDir)); + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index 8c290c2c87f..c046589a363 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -57,3 +57,11 @@ * jdk/jpackage/internal/WixToolTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.WixToolTest */ + +/* @test + * @summary RtfConverter unit tests + * @requires (os.family == "windows") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/RtfConverterTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.RtfConverterTest + */ diff --git a/test/jdk/tools/jpackage/windows/WinL10nTest.java b/test/jdk/tools/jpackage/windows/WinL10nTest.java index d63057adc44..6075b30a3b2 100644 --- a/test/jdk/tools/jpackage/windows/WinL10nTest.java +++ b/test/jdk/tools/jpackage/windows/WinL10nTest.java @@ -25,8 +25,11 @@ import static jdk.jpackage.test.WindowsHelper.getWixTypeFromVerboseJPackageOutpu import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; @@ -55,64 +58,103 @@ import jdk.jpackage.test.WindowsHelper.WixType; * --jpt-run=WinL10nTest */ -public class WinL10nTest { +public record WinL10nTest( + Collection wxlFileInitializers, + Collection expectedCultures, + Optional locale, + boolean enableWixUIExtension) { - public WinL10nTest(WixFileInitializer wxlFileInitializers[], - String[] expectedCultures, String expectedErrorMessage, - String userLanguage, String userCountry, - boolean enableWixUIExtension) { - this.wxlFileInitializers = wxlFileInitializers; - this.expectedCultures = expectedCultures; - this.expectedErrorMessage = expectedErrorMessage; - this.userLanguage = userLanguage; - this.userCountry = userCountry; - this.enableWixUIExtension = enableWixUIExtension; + public WinL10nTest { + Objects.requireNonNull(wxlFileInitializers); + Objects.requireNonNull(expectedCultures); + Objects.requireNonNull(locale); + } + + public WinL10nTest(Collection expectedCultures, Locale locale, boolean enableWixUIExtension) { + this(List.of(), expectedCultures, Optional.of(locale), enableWixUIExtension); + } + + public WinL10nTest(Collection wxlFileInitializers, Collection expectedCultures) { + this(wxlFileInitializers, expectedCultures, Optional.empty(), false); + } + + public WinL10nTest(WinL10nTest other) { + this(other.wxlFileInitializers, other.expectedCultures, other.locale, other.enableWixUIExtension); + } + + @Override + public String toString() { + var tokens = new ArrayList(); + if (!wxlFileInitializers.isEmpty()) { + tokens.add(String.format("wxlFileInitializers=%s", wxlFileInitializers)); + } + if (!expectedCultures.isEmpty()) { + tokens.add(String.format("expectedCultures=%s", expectedCultures)); + } + locale.ifPresent(l -> { + tokens.add(String.format("locale=%s", l)); + }); + if (enableWixUIExtension) { + tokens.add("enableWixUIExtension=true"); + } + return String.join(", ", tokens); } @Parameters public static List data() { - return List.of(new Object[][]{ - {null, new String[] {"en-us"}, null, null, null, false}, - {null, new String[] {"en-us"}, null, "en", "US", false}, - {null, new String[] {"en-us"}, null, "en", "US", true}, - {null, new String[] {"de-de"}, null, "de", "DE", false}, - {null, new String[] {"de-de"}, null, "de", "DE", true}, - {null, new String[] {"ja-jp"}, null, "ja", "JP", false}, - {null, new String[] {"ja-jp"}, null, "ja", "JP", true}, - {null, new String[] {"zh-cn"}, null, "zh", "CN", false}, - {null, new String[] {"zh-cn"}, null, "zh", "CN", true}, - {new WixFileInitializer[] { + + List testCases = new ArrayList<>(); + + testCases.add(new WinL10nTest(List.of(), List.of("en-us"), Optional.empty(), false)); + for (var enableWixUIExtension : List.of(true, false)) { + testCases.add(new WinL10nTest(List.of("en-us"), Locale.of("en", "US"), enableWixUIExtension)); + testCases.add(new WinL10nTest(List.of("de-de"), Locale.of("de", "DE"), enableWixUIExtension)); + testCases.add(new WinL10nTest(List.of("ja-jp"), Locale.of("ja", "JP"), enableWixUIExtension)); + testCases.add(new WinL10nTest(List.of("zh-cn"), Locale.of("zh", "CN"), enableWixUIExtension)); + } + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("a.wxl", "en-us") - }, new String[] {"en-us"}, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of("en-us"))); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("a.wxl", "fr") - }, new String[] {"fr", "en-us"}, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of("fr", "en-us"))); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("a.wxl", "fr"), WixFileInitializer.create("b.wxl", "fr") - }, new String[] {"fr", "en-us"}, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of("fr", "en-us"))); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("a.wxl", "it"), WixFileInitializer.create("b.wxl", "fr") - }, new String[] {"it", "fr", "en-us"}, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of("it", "fr", "en-us"))); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("c.wxl", "it"), WixFileInitializer.create("b.wxl", "fr") - }, new String[] {"fr", "it", "en-us"}, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of("fr", "it", "en-us"))); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("a.wxl", "fr"), WixFileInitializer.create("b.wxl", "it"), WixFileInitializer.create("c.wxl", "fr"), WixFileInitializer.create("d.wxl", "it") - }, new String[] {"fr", "it", "en-us"}, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of("fr", "it", "en-us"))); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("c.wxl", "it"), WixFileInitializer.createMalformed("b.wxl") - }, null, null, null, null, false}, - {new WixFileInitializer[] { + ), List.of())); + + testCases.add(new WinL10nTest(List.of( WixFileInitializer.create("MsiInstallerStrings_de.wxl", "de") - }, new String[] {"en-us"}, null, null, null, false} - }); + ), List.of("en-us"))); + + return testCases.stream().map(testCase -> { + return new Object[] { testCase }; + }).toList(); } private record OutputAnalizer(Executor.Result result, WixType wixType, Optional wixBuildCommandLine) { @@ -123,8 +165,8 @@ public class WinL10nTest { Objects.requireNonNull(wixBuildCommandLine); } - OutputAnalizer(Executor.Result result) { - this(result, getWixTypeFromVerboseJPackageOutput(result)); + OutputAnalizer(Locale locale, Executor.Result result) { + this(result, getWixTypeFromVerboseJPackageOutput(locale, result)); } OutputAnalizer(Executor.Result result, WixType wixType) { @@ -196,13 +238,7 @@ public class WinL10nTest { public void test() throws IOException { final Path tempRoot = TKit.createTempDirectory("tmp"); - final boolean allWxlFilesValid; - if (wxlFileInitializers != null) { - allWxlFilesValid = Stream.of(wxlFileInitializers).allMatch( - WixFileInitializer::isValid); - } else { - allWxlFilesValid = true; - } + final boolean allWxlFilesValid = wxlFileInitializers.stream().allMatch(WixFileInitializer::isValid); PackageTest test = new PackageTest() .forTypes(PackageType.WINDOWS) @@ -225,19 +261,18 @@ public class WinL10nTest { boolean withJavaOptions = false; // Set JVM default locale that is used to select primary l10n file. - if (userLanguage != null) { - withJavaOptions = true; - cmd.addArguments("-J-Duser.language=" + userLanguage); - } - if (userCountry != null) { - withJavaOptions = true; - cmd.addArguments("-J-Duser.country=" + userCountry); - } - - if (withJavaOptions) { + locale.ifPresent(l -> { + cmd.addArguments("-J-Duser.language=" + l.getLanguage()); + cmd.addArguments("-J-Duser.country=" + l.getCountry()); + // Force UTF8 encoding of the output of jpackage command. + // This is the default encoding of the output for the command executor. + // This is needed to properly handle JP and CN l10n-s. + cmd.addArguments("-J-Dstdout.encoding=UTF-8"); + cmd.addArguments("-J-Dstderr.encoding=UTF-8"); + cmd.addArguments("-J-Dfile.encoding=UTF-8"); // Use jpackage as a command to allow "-J" options come through cmd.useToolProvider(false); - } + }); // Cultures handling is affected by the WiX extensions used. // By default only WixUtilExtension is used, this flag @@ -252,14 +287,10 @@ public class WinL10nTest { }) .addBundleVerifier((cmd, result) -> { - var outputAnalizer = new OutputAnalizer(result); + var outputAnalizer = new OutputAnalizer(locale.orElseGet(Locale::getDefault), result); - if (expectedCultures != null) { - outputAnalizer.verifyCulturesInCmdline(expectedCultures); - } - - if (expectedErrorMessage != null) { - TKit.assertTextStream(expectedErrorMessage).apply(result.stderr()); + if (!expectedCultures.isEmpty()) { + outputAnalizer.verifyCulturesInCmdline(expectedCultures.toArray(String[]::new)); } if (wxlFileInitializers != null) { @@ -277,7 +308,7 @@ public class WinL10nTest { v.apply(List.of(outputAnalizer.getWixBuildCommandLine())); } } else { - Stream.of(wxlFileInitializers) + wxlFileInitializers.stream() .filter(Predicate.not(WixFileInitializer::isValid)) .forEach(v -> v.createCmdOutputVerifier( wixSrcDir).apply(result.getOutput())); @@ -287,9 +318,9 @@ public class WinL10nTest { } }); - if (wxlFileInitializers != null) { + if (!wxlFileInitializers.isEmpty()) { test.addInitializer(cmd -> { - resourceDir = TKit.createTempDirectory("resources"); + var resourceDir = TKit.createTempDirectory("resources"); cmd.addArguments("--resource-dir", resourceDir); @@ -299,21 +330,13 @@ public class WinL10nTest { }); } - if (expectedErrorMessage != null || !allWxlFilesValid) { + if (!allWxlFilesValid) { test.setExpectedExitCode(1); } test.run(); } - private final WixFileInitializer[] wxlFileInitializers; - private final String[] expectedCultures; - private final String expectedErrorMessage; - private final String userLanguage; - private final String userCountry; - private final boolean enableWixUIExtension; - private Path resourceDir; - private static class WixFileInitializer { static WixFileInitializer create(String name, String culture) { return new WixFileInitializer(name, culture); diff --git a/test/jdk/tools/launcher/Arrrghs.java b/test/jdk/tools/launcher/Arrrghs.java index fe1539ff5a9..1b730b85f48 100644 --- a/test/jdk/tools/launcher/Arrrghs.java +++ b/test/jdk/tools/launcher/Arrrghs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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 @@ * @test * @bug 5030233 6214916 6356475 6571029 6684582 6742159 4459600 6758881 6753938 * 6894719 6968053 7151434 7146424 8007333 8077822 8143640 8132379 8218547 + * 8385024 * @summary Argument parsing validation. * @modules jdk.compiler * jdk.zipfs @@ -497,6 +498,56 @@ public class Arrrghs extends TestHelper { TestResult tr = doExec(javaCmd, "-jar", elp.getAbsolutePath()); tr.checkPositive(); tr.contains("Hello from ELP"); + if (!tr.testStatus) { + throw new RuntimeException("testLongPathJarFile failed:\n" + tr.status); + } + } + + /* + * Tests -jar command where the path to the jar file is shorter than + * `MAX_PATH` (260 characters) but the absolute path to the file is longer + * than `MAX_PATH` + */ + + @Test + void testLongResolvedPathJarFile() throws IOException { + if (!isWindows) { + return; + } + + final int MAX_PATH = 260; + String jarName = "elp.jar"; + String dirSegment = "longpathtest_longpathtest/"; + int cwdLen = Path.of(System.getProperty("user.dir")).toString().length(); + + // We want `cwdLen + 1 + relativeLen` to be longer than `MAX_PATH` and + // `relativeLen` to be shorter than `MAX_PATH`, where `relativeLen` is + // `repeats * dirSegment.length() + jarName.length()`. + int minRelative = MAX_PATH - cwdLen; + int repeats = (minRelative - jarName.length() + dirSegment.length() - 1) / dirSegment.length(); + if (repeats < 1) repeats = 1; + int relativeLen = repeats * dirSegment.length() + jarName.length(); + int absoluteLen = cwdLen + 1 + relativeLen; + + if (relativeLen >= MAX_PATH || absoluteLen <= MAX_PATH) { + throw new RuntimeException("Error: invariant mismatch:" + + " cwdLen=" + cwdLen + + " relativeLen=" + relativeLen + + " absoluteLen=" + absoluteLen); + } + + String longPathStr = dirSegment.repeat(repeats); + Path longPath = Paths.get(longPathStr); + Path jarPath = Files.createDirectories(longPath).resolve(jarName); + File elp = jarPath.toFile(); + createJar(elp, new File("Foo"), "public static void main(String[] args){ System.out.println(\"Hello from ELP\"); }"); + + TestResult tr = doExec(javaCmd, "-jar", jarPath.toString()); + tr.checkPositive(); + tr.contains("Hello from ELP"); + if (!tr.testStatus) { + throw new RuntimeException("testLongResolvedPathJarFile failed:\n" + tr.status); + } } /* diff --git a/test/langtools/TEST.ROOT b/test/langtools/TEST.ROOT index c76f99d1396..8319e724e89 100644 --- a/test/langtools/TEST.ROOT +++ b/test/langtools/TEST.ROOT @@ -15,7 +15,7 @@ keys=intermittent randomness needs-src needs-src-jdk_javadoc groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library diff --git a/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java b/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java index 78c2bb9e3c8..d5997bd0d5c 100644 --- a/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java +++ b/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java @@ -24,7 +24,7 @@ /* * @test * @bug 8250768 8261976 8277300 8282452 8287597 8325325 8325874 8297879 - * 8331947 8281533 8343239 8318416 8346109 8359024 + * 8331947 8281533 8343239 8318416 8346109 8359024 8386589 * @summary test generated docs for items declared using preview * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -242,6 +242,42 @@ public class TestPreview extends JavadocTester { """); } + // 8386589 pre-existing permanent API that is later retrofitted + // to permit a @PreviewFeature API should not be flagged as a preview feature + @Test + public void nonPreviewPermitsPreview(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, """ + package p; + public sealed interface Core permits BasicChild, PreviewChild { + } + """, """ + package p; + public non-sealed interface BasicChild extends Core { + } + """,""" + package p; + import jdk.internal.javac.PreviewFeature; + @PreviewFeature(feature = PreviewFeature.Feature.TEST) + public non-sealed interface PreviewChild extends Core { + } + """); + javadoc("-d", "out-non-preview-permits-preview", + "--add-exports", "java.base/jdk.internal.javac=ALL-UNNAMED", + "--source-path", + src.toString(), + "p"); + checkExit(Exit.OK); + checkOutput("p/Core.html", false, + """ +
      Programs can only use Core when preview features are enabled.
      + """); + checkOutput("p/PreviewChild.html", true, + """ +
      Programs can only use PreviewChild when preview features are enabled.
      + """); + } + @Test public void test8277300() { javadoc("-d", "out-8277300", diff --git a/test/langtools/jdk/javadoc/doclet/testPreview/api/preview/NoPreview.java b/test/langtools/jdk/javadoc/doclet/testPreview/api/preview/NoPreview.java index b4ff5f0da00..0cae5744f62 100644 --- a/test/langtools/jdk/javadoc/doclet/testPreview/api/preview/NoPreview.java +++ b/test/langtools/jdk/javadoc/doclet/testPreview/api/preview/NoPreview.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -36,6 +36,6 @@ public class NoPreview { public static class T {} // Preview support feature without JEP should not be listed - @PreviewFeature(feature=Feature.LANGUAGE_MODEL) + @PreviewFeature(feature=Feature.PREVIEW_SUPPORT) public void supportMethod() {} } diff --git a/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java b/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java new file mode 100644 index 00000000000..2be04e98c2e --- /dev/null +++ b/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java @@ -0,0 +1,293 @@ +/* + * 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 Test javac incremental compilation with modules + * @run junit TestIncrementalComp + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TestIncrementalComp { + + static final ToolProvider JAVAC = ToolProvider.findFirst("javac") + .orElseThrow(); + + record TestCase(String srcDir, Map sources, Set modules, String mainModule, Map addReadsEdges) { + TestCase(String srcDir, Map sources, Set modules, String mainModule) { + this(srcDir, sources, modules, mainModule, Map.of()); + } + } + + @ParameterizedTest + @MethodSource("cases") + public void test(TestCase testCase) throws Throwable { + Path workDir = Path.of(testCase.srcDir()); + // set up test sources + Path localTestModules = workDir.resolve("test_modules"); + Path outDir = workDir.resolve("mods"); + for (Map.Entry sourceFile : testCase.sources().entrySet()) { + Path filePath = localTestModules.resolve(sourceFile.getKey()); + Files.createDirectories(filePath.getParent()); + Files.writeString(filePath, sourceFile.getValue(), CREATE_NEW); + } + + Path libPath = localTestModules.resolve(LIB_PATH); + Files.createDirectories(libPath.getParent()); + Files.writeString(libPath, ALT_LIB_INT, CREATE_NEW); + + List javacCommand = new ArrayList<>(List.of( + "-d", outDir.toString(), + "--module-source-path=" + localTestModules, + "--module", String.join(",", testCase.modules()) + )); + for (Map.Entry addReads : testCase.addReadsEdges().entrySet()) { + String reader = addReads.getKey(); + String read = addReads.getValue(); + javacCommand.add(String.format("--add-reads=%s=%s", reader, read)); + } + // compile both modules + compile(javacCommand); + + String mainClass = testCase.mainModule() + ".app.Main"; + invokeMainMethod(outDir, testCase.mainModule(), mainClass, testCase.addReadsEdges()); + + // modify sources. Dep is not modified + Files.writeString(libPath, ALT_LIB_LONG, TRUNCATE_EXISTING); + + // recompile. Any dependency on the changed file should be recompiled as well + compile(javacCommand); + + // should work + // if this fails because of incremental compilation issues, we can expect to see a NoSuchMethodError + invokeMainMethod(outDir, testCase.mainModule(), mainClass, testCase.addReadsEdges()); + } + + private static void invokeMainMethod(Path modulePath, String moduleName, String mainClassName, + Map addReadsEdges) + throws ReflectiveOperationException { + // define module layer + // note that we need to explicitly add any read module to the set of roots + ModuleLayer boot = ModuleLayer.boot(); + Set allRoots = Stream.concat(Stream.of(moduleName), addReadsEdges.values().stream()) + .collect(Collectors.toSet()); + Configuration config = boot.configuration() + .resolve(ModuleFinder.of(modulePath), ModuleFinder.of(), allRoots); + ModuleLayer.Controller controller = ModuleLayer.defineModulesWithOneLoader( + config, List.of(boot), ClassLoader.getSystemClassLoader()); + + // add extra reads edges + for (Map.Entry addReads : addReadsEdges.entrySet()) { + Module reader = controller.layer().findModule(addReads.getKey()).orElseThrow(); + Module read = controller.layer().findModule(addReads.getValue()).orElseThrow(); + controller.addReads(reader, read); + } + + // invoke main + Class main1 = controller.layer().findLoader(moduleName).loadClass(mainClassName); + Method m = main1.getMethod("main", String[].class); + m.invoke(null, new Object[]{ new String[0] }); + } + + private static void compile(List args) { + System.err.println("compile: " + args); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = JAVAC.run(pw, pw, args.toArray(String[]::new)); + pw.close(); + System.err.println(sw); + assertEquals(0, rc); + } + + private static final Path LIB_PATH = Path.of("org.moda/org/moda/lib/Lib.java"); + + private static final String ALT_LIB_INT = """ + package org.moda.lib; + public class Lib { + public static int getVal() { + return 42; + } + } + """; + + private static final String ALT_LIB_LONG = """ + package org.moda.lib; + public class Lib { + public static long getVal() { + return 42; + } + } + """; + + static Stream cases() { + return Stream.of( + new TestCase("single", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + // for reflective access + exports org.moda.app; + } + """, + Path.of("org.moda/org/moda/lib/Dep.java"), + """ + package org.moda.lib; + + public class Dep { + public static long getVal() { + return Lib.getVal(); + } + } + """, + Path.of("org.moda/org/moda/app/Main.java"), + """ + package org.moda.app; + + import org.moda.lib.Dep; + + public class Main { + public static void main(String[] args) { + System.out.println(Dep.getVal()); + } + } + """ + ), Set.of("org.moda"), "org.moda"), + new TestCase("multi", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + requires org.moda; + + // for reflective access + exports org.modb.app; + } + """, + Path.of("org.modb/org/modb/app/Main.java"), + """ + package org.modb.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb"), "org.modb"), + new TestCase("transitive", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + // for org.modc + requires transitive org.moda; + } + """, + Path.of("org.modc/module-info.java"), + """ + module org.modc { + requires org.modb; + + // for reflective access + exports org.modc.app; + } + """, + Path.of("org.modc/org/modc/app/Main.java"), + """ + package org.modc.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb", "org.modc"), "org.modc"), + new TestCase("add_reads", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + // no explicit requires + + // for reflective access + exports org.modb.app; + } + """, + Path.of("org.modb/org/modb/app/Main.java"), + """ + package org.modb.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb"), "org.modb", Map.of("org.modb", "org.moda")) + ); + } + } diff --git a/test/langtools/tools/javac/SystemFilesClosed.java b/test/langtools/tools/javac/SystemFilesClosed.java new file mode 100644 index 00000000000..a08828c842d --- /dev/null +++ b/test/langtools/tools/javac/SystemFilesClosed.java @@ -0,0 +1,129 @@ +/* + * 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 8357249 + * @summary Check that `lib/jrt-fs.jar` and `lib/modules` are properly closed while + * javac is invoked with `--system` option. + * @requires os.family == "mac" | os.family == "linux" + * @run junit ${test.main.class} + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class SystemFilesClosed { + + private Path base; + + @Test + void testSystemFilesClosed() throws Exception { + // Probe lsof availability before doing the jlink/compile work + if (!lsofCommand().isPresent()) { + Assumptions.abort("lsof command is not available on this system"); + } + + String targetSystem = base.toString(); + int ret = java.util.spi.ToolProvider.findFirst("jlink") + .orElseThrow() + .run(System.out, System.err, "--add-modules", "java.base", "--output", targetSystem); + if (ret != 0) { + System.out.println("It is most probably an exploded build. Skip testing."); + return; + } + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + SimpleJavaFileObject compilationUnit = new SimpleJavaFileObject(URI.create("string:///Test.java"), JavaFileObject.Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return """ + public class Test { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + """; + } + }; + + try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { + Assertions.assertEquals(true, + compiler.getTask(null, fileManager, null, List.of("--system", targetSystem), null, List.of(compilationUnit)).call(), + "Compilation task failed"); + } + + Process process = new ProcessBuilder() + .command(lsofCommand().orElseThrow(() -> new RuntimeException("lsof command is not available on this system")), + "-p", String.valueOf(ProcessHandle.current().pid())) + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + List lines; + String realPath = base.toRealPath().toString(); + try (InputStream stdout = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stdout))) { + lines = reader.lines().filter(line -> line.contains(realPath)).toList(); + } + process.waitFor(); + Assertions.assertEquals(0, lines.size(), "File(s) remain opened: " + lines); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } + + static Optional lsofCommandCache = Arrays.stream(new String[] { + "/usr/bin/lsof", + "/usr/sbin/lsof", + "/bin/lsof", + "/sbin/lsof", + "/usr/local/bin/lsof"}) + .filter(args -> new File(args).exists()) + .findFirst(); + + static Optional lsofCommand() { + return lsofCommandCache; + } +} diff --git a/test/langtools/tools/javac/api/TestJavacTask_Close.java b/test/langtools/tools/javac/api/TestJavacTask_Close.java new file mode 100644 index 00000000000..0b331498d0a --- /dev/null +++ b/test/langtools/tools/javac/api/TestJavacTask_Close.java @@ -0,0 +1,171 @@ +/* + * 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 8173155 + * @summary Cannot release resources after partial compilation + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.JarTask toolbox.JavacTask toolbox.ToolBox + * @run junit ${test.main.class} + */ + +import java.io.Closeable; +import java.io.IOException; +import java.net.URI; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; +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.ToolBox; +import toolbox.JarTask; +import toolbox.JavacTask; + + +public class TestJavacTask_Close { + + private Path base; + + @Test + void testClose() throws Exception { + Path jar = createJar(); + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavaFileObject compilationUnit = SimpleJavaFileObject.forSource(URI.create("string:///Test.java"), + """ + public class Test { + private Lib lib; + } + """); + + boolean[] state = new boolean[] {false, false}; + try (FM fm = new FM(compiler.getStandardFileManager(null, null, null), state)) { + com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask) compiler.getTask( + null, fm, null, List.of("-classpath", jar.toString()), null, List.of(compilationUnit)); + Assertions.assertNotNull(task.parse(), "parse() failed"); + task.close(); + Assertions.assertThrows(IllegalStateException.class, () -> task.analyze(), "analyze() on closed task"); + } + + Assertions.assertTrue(state[0], "URLClassLoader not created"); + Assertions.assertTrue(state[1], "URLClassLoader not closed"); + } + + @Test + void testRepeatedClose() throws Exception { + Path jar = createJar(); + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavaFileObject compilationUnit = SimpleJavaFileObject.forSource(URI.create("string:///Test.java"), + """ + public class Test { + private Lib lib; + } + """); + + boolean[] state = new boolean[] {false, false}; + try (FM fm = new FM(compiler.getStandardFileManager(null, null, null), state); + com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask) compiler.getTask( + null, fm, null, List.of("-classpath", jar.toString()), null, List.of(compilationUnit))) { + Assertions.assertEquals(true, task.call(), "Compilation task failed"); + } + + Assertions.assertTrue(state[0], "URLClassLoader not created"); + Assertions.assertTrue(state[1], "URLClassLoader not closed"); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } + + private Path createJar() throws IOException { + Path jarSrc = base.resolve("jarSrc"); + Path jarClasses = base.resolve("jarClasses"); + Path jar = base.resolve("jar.jar"); + Files.createDirectories(jarClasses); + + ToolBox tb = new ToolBox(); + tb.writeJavaFiles(jarSrc, "public class Lib { }"); + + new JavacTask(tb) + .outdir(jarClasses) + .files(tb.findJavaFiles(jarSrc)) + .run() + .writeAll(); + new JarTask(tb) + .run("cf", jar.toString(), "-C", jarClasses.toString(), "."); + + return jar; + } + + private static class FM extends ForwardingJavaFileManager { + + private final boolean[] state; + + private FM(JavaFileManager fileManager, boolean[] state) { + super(fileManager); + this.state = state; + } + + @Override + public ClassLoader getClassLoader(Location location) { + ClassLoader cl = super.getClassLoader(location); + return cl instanceof URLClassLoader urlCl ? new CL(urlCl, state) : cl; + } + } + + private static class CL extends ClassLoader implements Closeable { + + private final URLClassLoader urlCl; + private final boolean[] state; + + private CL(URLClassLoader urlCl, boolean[] state) { + this.urlCl = urlCl; + this.state = state; + this.state[0] = true; + } + + @Override + public void close() throws IOException { + state[1] = true; + urlCl.close(); + } + } +} diff --git a/test/langtools/tools/javac/modules/MOptionTest.java b/test/langtools/tools/javac/modules/MOptionTest.java index ff08960b0bc..470815a9493 100644 --- a/test/langtools/tools/javac/modules/MOptionTest.java +++ b/test/langtools/tools/javac/modules/MOptionTest.java @@ -84,12 +84,12 @@ public class MOptionTest extends ModuleTestBase { .run(Task.Expect.SUCCESS) .writeAll(); - if (!moduleInfoTimeStamp.equals(Files.getLastModifiedTime(moduleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(moduleInfoClass).compareTo(moduleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!testTestTimeStamp.equals(Files.getLastModifiedTime(testTestClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(testTestClass).compareTo(Files.getLastModifiedTime(testTest)) < 0) { + throw new AssertionError("Classfiles too old!"); } // Date back the source file by one second compared to the current time. @@ -102,8 +102,8 @@ public class MOptionTest extends ModuleTestBase { .run(Task.Expect.SUCCESS) .writeAll(); - if (!moduleInfoTimeStamp.equals(Files.getLastModifiedTime(moduleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(moduleInfoClass).compareTo(moduleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } if (Files.getLastModifiedTime(testTestClass).compareTo(Files.getLastModifiedTime(testTest)) < 0) { @@ -219,20 +219,20 @@ public class MOptionTest extends ModuleTestBase { .run(Task.Expect.SUCCESS) .writeAll(); - if (!m1ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m1ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m1ModuleInfoClass).compareTo(m1ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!m2ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m2ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m2ModuleInfoClass).compareTo(m2ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!C1TimeStamp.equals(Files.getLastModifiedTime(classC1))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(classC1).compareTo(Files.getLastModifiedTime(C1Source)) < 0) { + throw new AssertionError("Classfiles too old!"); } - if (!C2TimeStamp.equals(Files.getLastModifiedTime(classC2))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(classC2).compareTo(Files.getLastModifiedTime(C2Source)) < 0) { + throw new AssertionError("Classfiles too old!"); } // Date back the source file by one second compared to the current time. @@ -246,12 +246,12 @@ public class MOptionTest extends ModuleTestBase { .run(Task.Expect.SUCCESS) .writeAll(); - if (!m1ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m1ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m1ModuleInfoClass).compareTo(m1ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!m2ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m2ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m2ModuleInfoClass).compareTo(m2ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } if (Files.getLastModifiedTime(classC1).compareTo(Files.getLastModifiedTime(C1Source)) < 0) { diff --git a/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java b/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java new file mode 100644 index 00000000000..37d1541f18a --- /dev/null +++ b/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java @@ -0,0 +1,215 @@ +/* + * 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 8387215 + * @summary Check that javac does not report invalid errors when compiling a valid + * compact record constructor when an on-demand attribution is triggered + * by an annotation processor calling Trees.getElement(...) for identifiers + * inside the constructor. + * @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 com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +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.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.Task; + +public class OnDemandAttributionRecordConstructor { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testCompactRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + record Repro(String name) { + Repro { + name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testCompactRecordConstructorWithoutGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString(), "-AskipGetElement=true") + .sources(""" + record Repro(String name) { + Repro { + name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testCanonicalRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + record Repro(String name) { + Repro(String name) { + this.name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testBrokenRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + record Repro(String name) { + Repro(String name) { + super(); //illegal + this.name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Repro.java:2:5: compiler.err.invalid.canonical.constructor.in.record: (compiler.misc.canonical), Repro, (compiler.misc.canonical.must.not.contain.explicit.constructor.invocation)", + "1 error")); + } + + @SupportedAnnotationTypes("*") + @SupportedOptions(ProcessorImpl.SKIP_GET_ELEMENT) + private static class ProcessorImpl extends AbstractProcessor { + + private static final String SKIP_GET_ELEMENT = "skipGetElement"; + private Trees trees; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + trees = Trees.instance(processingEnv); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return false; + } + for (Element rootElement : roundEnv.getRootElements()) { + TreePath rootPath = trees.getPath(rootElement); + if (rootPath == null) { + continue; + } + new TreePathScanner() { + @Override + public Void visitIdentifier(IdentifierTree node, Void unused) { + TreePath currentPath = getCurrentPath(); + if (!skipGetElement() && insideRecordConstructor(currentPath)) { + processingEnv.getMessager() + .printMessage(Diagnostic.Kind.NOTE, + "Calling Trees.getElement for identifier '" + node.getName() + + "' inside a record constructor"); + trees.getElement(currentPath); + } + return super.visitIdentifier(node, unused); + } + }.scan(rootPath, null); + } + return false; + } + + private boolean skipGetElement() { + return Boolean.parseBoolean(processingEnv.getOptions().get(SKIP_GET_ELEMENT)); + } + + private static boolean insideRecordConstructor(TreePath path) { + TreePath current = path; + while (current != null) { + if (current.getLeaf() instanceof MethodTree method + && method.getReturnType() == null + && current.getParentPath() != null + && current.getParentPath().getLeaf().getKind() == Tree.Kind.RECORD) { + return true; + } + current = current.getParentPath(); + } + return false; + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/recovery/AttrRecovery.java b/test/langtools/tools/javac/recovery/AttrRecovery.java index 64aaad2a184..fb852b5b3f2 100644 --- a/test/langtools/tools/javac/recovery/AttrRecovery.java +++ b/test/langtools/tools/javac/recovery/AttrRecovery.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8301580 8322159 8333107 8332230 8338678 8351260 8366196 8372336 8373094 8384229 + * @bug 8301580 8322159 8333107 8332230 8338678 8351260 8366196 8372336 8373094 8384229 8387865 * @summary Verify error recovery w.r.t. Attr * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -860,6 +860,33 @@ public class AttrRecovery { .writeAll(); } + @Test //JDK-8387865 + public void testThisEscapeUnknownField() throws Exception { + String code = """ + public class C { + public C() { + this.unknown = unknown; + } + } + """; + List actual = new JavacTask(tb) + .options("-XDrawDiagnostics", "-XDdev", + "-XDshould-stop.at=WARN", "-Xlint:this-escape") + .sources(code) + .outdir(base) + .run(Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = List.of( + "C.java:3:13: compiler.err.cant.resolve: kindname.variable, unknown, , ", + "C.java:3:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, C, null)", + "2 errors" + ); + + assertEquals(expected, actual); + } + @BeforeEach public void setUp(TestInfo info) throws IOException { base = Path.of(info.getTestMethod().orElseThrow().getName()); diff --git a/test/langtools/tools/jdeps/SystemFilesClosed.java b/test/langtools/tools/jdeps/SystemFilesClosed.java new file mode 100644 index 00000000000..f9585a10f62 --- /dev/null +++ b/test/langtools/tools/jdeps/SystemFilesClosed.java @@ -0,0 +1,109 @@ +/* + * 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 8386334 + * @summary Check that `lib/jrt-fs.jar` and `lib/modules` are properly closed while + * jdeps is invoked with `--system` option. + * @requires os.family == "mac" | os.family == "linux" + * @modules jdk.jdeps + * @library lib + * @build JdepsRunner + * @run junit ${test.main.class} + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class SystemFilesClosed { + + private Path base; + + @Test + void testSystemFilesClosed() throws Exception { + // Probe lsof availability before doing the jlink/jdeps work + if (!lsofCommand().isPresent()) { + Assumptions.abort("lsof command is not available on this system"); + } + + String targetSystem = base.toString(); + int ret = java.util.spi.ToolProvider.findFirst("jlink") + .orElseThrow() + .run(System.out, System.err, "--add-modules", "java.base", "--output", targetSystem); + if (ret != 0) { + System.out.println("It is most probably an exploded build. Skip testing."); + return; + } + + JdepsRunner jdeps = new JdepsRunner("--check", "java.base", "--system", targetSystem); + Assertions.assertEquals(0, jdeps.run(true), "Jdeps task failed"); + + Process process = new ProcessBuilder() + .command(lsofCommand().orElseThrow(() -> new RuntimeException("lsof command is not available on this system")), + "-p", String.valueOf(ProcessHandle.current().pid())) + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + List lines; + String realPath = base.toRealPath().toString(); + try (InputStream stdout = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stdout))) { + lines = reader.lines().filter(line -> line.contains(realPath)).toList(); + } + process.waitFor(); + Assertions.assertEquals(0, lines.size(), "File(s) remain opened: " + lines); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } + + static Optional lsofCommandCache = Arrays.stream(new String[] { + "/usr/bin/lsof", + "/usr/sbin/lsof", + "/bin/lsof", + "/sbin/lsof", + "/usr/local/bin/lsof"}) + .filter(args -> new File(args).exists()) + .findFirst(); + + static Optional lsofCommand() { + return lsofCommandCache; + } +} diff --git a/test/lib-test/TEST.ROOT b/test/lib-test/TEST.ROOT index 33c9a9c2a43..9c9db2998a5 100644 --- a/test/lib-test/TEST.ROOT +++ b/test/lib-test/TEST.ROOT @@ -29,7 +29,7 @@ keys=randomness # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Prevent TestNG-based tests under this root, use @run junit actions instead disallowedActions=testng diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 59e4a1bbbde..8060eb92a87 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -703,7 +703,7 @@ public class CDSTestUtils { static String getCrashMessage(String stdOut) { int start = stdOut.indexOf("# A fatal error has been detected by the Java Runtime Environment:"); - int end = stdOut.indexOf(".log", start) + 4; + int end = stdOut.indexOf("# JRE version", start); return stdOut.substring(start, end); } diff --git a/test/lib/jdk/test/lib/security/TestTLSHandshake.java b/test/lib/jdk/test/lib/security/TestTLSHandshake.java index 1b36f66f7e0..30224ac9d74 100644 --- a/test/lib/jdk/test/lib/security/TestTLSHandshake.java +++ b/test/lib/jdk/test/lib/security/TestTLSHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, 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,14 +25,12 @@ package jdk.test.lib.security; import java.io.*; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; - public final class TestTLSHandshake extends SSLSocketTest { - public static final String CIPHER_SUITE = - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; public static final long CERT_ID = Integer.toUnsignedLong(-1057291798); public static final long ANCHOR_CERT_ID = Integer.toUnsignedLong(1688661792); public static final String CERT_SERIAL = "00:ed:be:c8:f7:05:af:25:14"; @@ -41,6 +39,8 @@ public final class TestTLSHandshake extends SSLSocketTest { public String protocolVersion; public String peerHost; public int peerPort; + public String cipherSuite; + public String namedGroup; @Override protected void runServerApplication(SSLSocket socket) throws Exception { @@ -54,7 +54,17 @@ public final class TestTLSHandshake extends SSLSocketTest { @Override protected void runClientApplication(SSLSocket socket) throws Exception { - socket.setEnabledCipherSuites(new String[] { CIPHER_SUITE }); + SSLParameters params = socket.getSSLParameters(); + if (protocolVersion != null) { + params.setProtocols(new String [] { protocolVersion }); + } + if (cipherSuite != null) { + params.setCipherSuites(new String[] { cipherSuite }); + } + if (namedGroup != null) { + params.setNamedGroups(new String[] { namedGroup }); + } + socket.setSSLParameters(params); InputStream sslIS = socket.getInputStream(); OutputStream sslOS = socket.getOutputStream(); @@ -66,5 +76,6 @@ public final class TestTLSHandshake extends SSLSocketTest { protocolVersion = sslSession.getProtocol(); peerHost = sslSession.getPeerHost(); peerPort = sslSession.getPeerPort(); + cipherSuite = sslSession.getCipherSuite(); } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java index 13cc9a25113..856667a4e8b 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.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 @@ -34,9 +34,9 @@ import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import sun.security.util.RawKeySpec; import java.security.KeyFactory; +import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.util.HexFormat; @@ -64,7 +64,23 @@ public class HSSBench { public static Signature getVerifier(byte[] pk) throws Exception { var kf = KeyFactory.getInstance("HSS/LMS", Security.getProvider("SUN")); - var pk1 = kf.generatePublic(new RawKeySpec(pk)); + var pk1 = (PublicKey) kf.translateKey(new PublicKey() { + + @Override + public String getAlgorithm() { + return "HSS/LMS"; + } + + @Override + public String getFormat() { + return "RAW"; + } + + @Override + public byte[] getEncoded() { + return pk.clone(); + } + }); var vv = Signature.getInstance("HSS/LMS"); vv.initVerify(pk1); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16VectorOperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16VectorOperationsBenchmark.java new file mode 100644 index 00000000000..445e57cabf0 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16VectorOperationsBenchmark.java @@ -0,0 +1,354 @@ +/* + * 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.lang; + +import java.util.stream.IntStream; +import java.util.concurrent.TimeUnit; +import jdk.incubator.vector.*; +import org.openjdk.jmh.annotations.*; +import static jdk.incubator.vector.Float16.*; +import static java.lang.Float.*; + +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@Fork(jvmArgs = {"--add-modules=jdk.incubator.vector", "-Xbatch", "-XX:-TieredCompilation"}) +public class Float16VectorOperationsBenchmark { + @Param({"1024", "2057"}) + int vectorDim; + + int [] rexp; + short [] vectorRes; + short [] vector1; + short [] vector2; + short [] vector3; + short [] vector4; + short [] vector5; + boolean [] vectorPredicate; + + static final short f16_one = Float.floatToFloat16(1.0f); + static final short f16_two = Float.floatToFloat16(2.0f); + + @Setup(Level.Trial) + public void BmSetup() { + rexp = new int[vectorDim]; + vectorRes = new short[vectorDim]; + vector1 = new short[vectorDim]; + vector2 = new short[vectorDim]; + vector3 = new short[vectorDim]; + vector4 = new short[vectorDim]; + vector5 = new short[vectorDim]; + vectorPredicate = new boolean[vectorDim]; + + IntStream.range(0, vectorDim).forEach(i -> {vector1[i] = Float.floatToFloat16((float)i);}); + IntStream.range(0, vectorDim).forEach(i -> {vector2[i] = Float.floatToFloat16((float)i);}); + IntStream.range(0, vectorDim).forEach(i -> {vector3[i] = Float.floatToFloat16((float)i);}); + IntStream.range(0, vectorDim).forEach(i -> {vector4[i] = ((i & 0x1) == 0) ? + float16ToRawShortBits(Float16.POSITIVE_INFINITY) : + Float.floatToFloat16((float)i);}); + IntStream.range(0, vectorDim).forEach(i -> {vector5[i] = ((i & 0x1) == 0) ? + float16ToRawShortBits(Float16.NaN) : + Float.floatToFloat16((float)i);}); + // Special Values + Float16 [] specialValues = {Float16.NaN, Float16.NEGATIVE_INFINITY, Float16.valueOf(0.0), Float16.valueOf(-0.0), Float16.POSITIVE_INFINITY}; + IntStream.range(0, vectorDim).forEach( + i -> { + if ((i % 64) == 0) { + int idx1 = i % specialValues.length; + int idx2 = (i + 1) % specialValues.length; + int idx3 = (i + 2) % specialValues.length; + vector1[i] = float16ToRawShortBits(specialValues[idx1]); + vector2[i] = float16ToRawShortBits(specialValues[idx2]); + vector3[i] = float16ToRawShortBits(specialValues[idx3]); + } + } + ); + } + + static final VectorSpecies HSPECIES = Float16Vector.SPECIES_PREFERRED; + static final VectorSpecies FSPECIES = FloatVector.SPECIES_PREFERRED; + + @Benchmark + public void addBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.ADD, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.ADD, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public void subBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.SUB, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.SUB, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + } + + @Benchmark + public void mulBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.MUL, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.MUL, + Float16Vector.fromArray(HSPECIES, vector2, i, mask)) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public void divBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.DIV, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.DIV, + Float16Vector.fromArray(HSPECIES, vector2, i, mask)) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public void fmaBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(HSPECIES, vector2, i), + Float16Vector.fromArray(HSPECIES, vector3, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(HSPECIES, vector2, i, mask), + Float16Vector.fromArray(HSPECIES, vector3, i, mask)) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public void maxBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.MAX, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.MAX, + Float16Vector.fromArray(HSPECIES, vector2, i, mask)) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public void minBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.MIN, + Float16Vector.fromArray(HSPECIES, vector2, i)) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.MIN, + Float16Vector.fromArray(HSPECIES, vector2, i, mask)) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public void sqrtBenchmark() { + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.SQRT) + .intoArray(vectorRes, i); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.SQRT) + .intoArray(vectorRes, i, mask); + } + } + + @Benchmark + public short cosineSimilarityDoubleRoundingFP16() { + int i = 0; + Float16Vector macResVec = Float16Vector.broadcast(HSPECIES, (short)0); + Float16Vector vector1SquareVec = Float16Vector.broadcast(HSPECIES, (short)0); + Float16Vector vector2SquareVec = Float16Vector.broadcast(HSPECIES, (short)0); + // cosine distance = (VEC1 . VEC2) / ||VEC1||.||VEC2|| + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + // Explicit add and multiply operation ensures double rounding. + Float16Vector vec1 = Float16Vector.fromArray(HSPECIES, vector1, i); + Float16Vector vec2 = Float16Vector.fromArray(HSPECIES, vector2, i); + macResVec = vec1.lanewise(VectorOperators.MUL, vec2) + .lanewise(VectorOperators.ADD, macResVec); + vector1SquareVec = vec1.lanewise(VectorOperators.MUL, vec1) + .lanewise(VectorOperators.ADD, vector1SquareVec); + vector2SquareVec = vec2.lanewise(VectorOperators.MUL, vec2) + .lanewise(VectorOperators.ADD, vector2SquareVec); + } + + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector vec1 = Float16Vector.fromArray(HSPECIES, vector1, i, mask); + Float16Vector vec2 = Float16Vector.fromArray(HSPECIES, vector2, i, mask); + macResVec = vec1.lanewise(VectorOperators.MUL, vec2) + .lanewise(VectorOperators.ADD, macResVec); + vector1SquareVec = vec1.lanewise(VectorOperators.MUL, vec1) + .lanewise(VectorOperators.ADD, vector1SquareVec); + vector2SquareVec = vec2.lanewise(VectorOperators.MUL, vec2) + .lanewise(VectorOperators.ADD, vector2SquareVec); + return macResVec.lanewise(VectorOperators.DIV, + vector1SquareVec.lanewise(VectorOperators.MUL, + vector2SquareVec)) + .reduceLanes(VectorOperators.ADD, mask); + } else { + return macResVec.lanewise(VectorOperators.DIV, + vector1SquareVec.lanewise(VectorOperators.MUL, + vector2SquareVec)) + .reduceLanes(VectorOperators.ADD); + } + } + + @Benchmark + public short cosineSimilaritySingleRoundingFP16() { + int i = 0; + Float16Vector macResVec = Float16Vector.broadcast(HSPECIES, (short)0); + Float16Vector vector1SquareVec = Float16Vector.broadcast(HSPECIES, (short)0); + Float16Vector vector2SquareVec = Float16Vector.broadcast(HSPECIES, (short)0); + // cosine distance = (VEC1 . VEC2) / ||VEC1||.||VEC2|| + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + // Explicit add and multiply operation ensures double rounding. + Float16Vector vec1 = Float16Vector.fromArray(HSPECIES, vector1, i); + Float16Vector vec2 = Float16Vector.fromArray(HSPECIES, vector2, i); + macResVec = vec1.lanewise(VectorOperators.FMA, vec2, macResVec); + vector1SquareVec = vec1.lanewise(VectorOperators.FMA, vec1, vector1SquareVec); + vector2SquareVec = vec2.lanewise(VectorOperators.FMA, vec2, vector2SquareVec); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector vec1 = Float16Vector.fromArray(HSPECIES, vector1, i, mask); + Float16Vector vec2 = Float16Vector.fromArray(HSPECIES, vector2, i, mask); + macResVec = vec1.lanewise(VectorOperators.FMA, vec2, macResVec); + vector1SquareVec = vec1.lanewise(VectorOperators.FMA, vec1, vector1SquareVec); + vector2SquareVec = vec2.lanewise(VectorOperators.FMA, vec2, vector2SquareVec); + return macResVec.lanewise(VectorOperators.DIV, + vector1SquareVec.lanewise(VectorOperators.MUL, + vector2SquareVec)) + .reduceLanes(VectorOperators.ADD, mask); + } else { + return macResVec.lanewise(VectorOperators.DIV, + vector1SquareVec.lanewise(VectorOperators.MUL, + vector2SquareVec)) + .reduceLanes(VectorOperators.ADD); + } + } + + @Benchmark + public short euclideanDistanceFP16() { + Float16Vector resVec = Float16Vector.broadcast(HSPECIES, (short)0); + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + Float16Vector diffVec = Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.SUB, + Float16Vector.fromArray(HSPECIES, vector2, i)); + resVec = diffVec.lanewise(VectorOperators.FMA, diffVec, resVec); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + Float16Vector diffVec = Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.SUB, + Float16Vector.fromArray(HSPECIES, vector2, i, mask)); + resVec = diffVec.lanewise(VectorOperators.FMA, diffVec, resVec); + return resVec.lanewise(VectorOperators.SQRT) + .reduceLanes(VectorOperators.ADD, mask); + } else { + return resVec.lanewise(VectorOperators.SQRT) + .reduceLanes(VectorOperators.ADD); + } + } + + @Benchmark + public short dotProductFP16() { + Float16Vector distResVec = Float16Vector.broadcast(HSPECIES, (short)0); + int i = 0; + for (; i < HSPECIES.loopBound(vectorDim); i += HSPECIES.length()) { + distResVec = Float16Vector.fromArray(HSPECIES, vector1, i) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(HSPECIES, vector2, i), + distResVec); + } + if (i < vectorDim) { + VectorMask mask = HSPECIES.indexInRange(i, vectorDim); + distResVec = Float16Vector.fromArray(HSPECIES, vector1, i, mask) + .lanewise(VectorOperators.FMA, + Float16Vector.fromArray(HSPECIES, vector2, i, mask), + distResVec); + return distResVec.reduceLanes(VectorOperators.ADD, mask); + } else { + return distResVec.reduceLanes(VectorOperators.ADD); + } + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskedLogicOpts.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskedLogicOpts.java index 7fc1fbce2e4..3d8d1265c69 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskedLogicOpts.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskedLogicOpts.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 @@ -55,8 +55,9 @@ public class MaskedLogicOpts { int int512_arr_idx; int int256_arr_idx; int int128_arr_idx; - int long256_arr_idx; int long512_arr_idx; + int long256_arr_idx; + int long128_arr_idx; private Random r = new Random(1024); @@ -65,8 +66,9 @@ public class MaskedLogicOpts { int512_arr_idx = -16; int256_arr_idx = -8; int128_arr_idx = -4; - long256_arr_idx = -4; long512_arr_idx = -8; + long256_arr_idx = -4; + long128_arr_idx = -2; mask_arr = new boolean[ARRAYLEN]; i1 = new int[ARRAYLEN]; @@ -106,6 +108,7 @@ public class MaskedLogicOpts { int128_arr_idx = (((ARRAYLEN & ~3) - int128_arr_idx) <= 4) ? 0 : int128_arr_idx + 4; long512_arr_idx = (((ARRAYLEN & ~7) - long512_arr_idx) <= 8) ? 0 : long512_arr_idx + 8; long256_arr_idx = (((ARRAYLEN & ~3) - long256_arr_idx) <= 4) ? 0 : long256_arr_idx + 4; + long128_arr_idx = (((ARRAYLEN & ~1) - long128_arr_idx) <= 2) ? 0 : long128_arr_idx + 2; } @CompilerControl(CompilerControl.Mode.INLINE) @@ -278,6 +281,11 @@ public class MaskedLogicOpts { partiallyMaskedLogicOperationsLongKernel(LongVector.SPECIES_256, long256_arr_idx); } + @Benchmark + public void partiallyMaskedLogicOperationsLong128() { + partiallyMaskedLogicOperationsLongKernel(LongVector.SPECIES_128, long128_arr_idx); + } + @CompilerControl(CompilerControl.Mode.INLINE) public void bitwiseBlendOperationLongKernel(VectorSpecies SPECIES, int index) { VectorMask lmask = VectorMask.fromArray(SPECIES, mask_arr, index); @@ -305,4 +313,9 @@ public class MaskedLogicOpts { public void bitwiseBlendOperationLong256() { bitwiseBlendOperationLongKernel(LongVector.SPECIES_256, long256_arr_idx); } + + @Benchmark + public void bitwiseBlendOperationLong128() { + bitwiseBlendOperationLongKernel(LongVector.SPECIES_128, long128_arr_idx); + } } diff --git a/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java b/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java new file mode 100644 index 00000000000..c4f4e522409 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.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. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * Benchmarks patterns that may fuse low/high 64-bit multiply operations. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3) +public class MultiplyHighLowFusion { + + @Param("1024") + private int arraySize; + + private long[] lhs; + private long[] rhs; + + @Setup + public void setup() { + Random random = new Random(0x5EED); + lhs = new long[arraySize]; + rhs = new long[arraySize]; + for (int i = 0; i < arraySize; i++) { + lhs[i] = random.nextLong(); + rhs[i] = random.nextLong(); + } + } + + @Benchmark + public long signedLowOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += lhs[i] * rhs[i]; + } + return sum; + } + + @Benchmark + public long signedHighOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += Math.multiplyHigh(lhs[i], rhs[i]); + } + return sum; + } + + @Benchmark + public long signedLowPlusHigh() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + long a = lhs[i]; + long b = rhs[i]; + sum += (a * b) + Math.multiplyHigh(a, b); + } + return sum; + } + + @Benchmark + public long unsignedHighOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += Math.unsignedMultiplyHigh(lhs[i], rhs[i]); + } + return sum; + } + + @Benchmark + public long unsignedLowPlusHigh() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + long a = lhs[i]; + long b = rhs[i]; + sum += (a * b) + Math.unsignedMultiplyHigh(a, b); + } + return sum; + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java index a60ecc0f41a..fabd4360abb 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java @@ -541,21 +541,34 @@ public class VectorAlgorithmsImpl { int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W var vresult = IntVector.zero(SPECIES_I); + final boolean isLE = java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN; int i; for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { var vb = ByteVector.fromArray(SPECIES_B, a, i); // Add 128 to each byte. var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) .reinterpretAsShorts(); - // Each short lane contains 2 bytes, crunch them. - var vi = vs.and((short)0xff) // lower byte - .mul((short)31) - .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte - .reinterpretAsInts(); - // Each int contains 2 shorts, crunch them. - var v = vi.and(0xffff) // lower short - .mul(31 * 31) - .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Each short lane contains 2 bytes. + // Extract them in logical byte order (b0, b1), independent of platform endianness. + ShortVector firstByte = isLE ? vs.and((short)0xff) // b0 + : vs.lanewise(VectorOperators.LSHR, 8); // b0 on BE + ShortVector secondByte = isLE ? vs.lanewise(VectorOperators.LSHR, 8) // b1 + : vs.and((short)0xff); // b1 on BE + // Combine each byte pair into a pairwise hash value. + var vi = firstByte.mul((short)31) + .add(secondByte) + .reinterpretAsInts(); + // Each int lane contains two pairswise hash chunks: + // p0 = b0 * 31 + b1 + // p1 = b2 * 31 + b3 + // Extract them in logical order, independent of platform endianness. + IntVector pair0 = isLE ? vi.and(0xffff) // p0 + : vi.lanewise(VectorOperators.LSHR, 16); // p0 on BE + IntVector pair1 = isLE ? vi.lanewise(VectorOperators.LSHR, 16) // p1 + : vi.and(0xffff); // p1 on BE + // Crunch the pairwise results into one value. + var v = pair0.mul(31 * 31) + .add(pair1); // Add the correction for the 128 additions above. v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); // Every element of v now contains a crunched int-package of 4 bytes.