diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index 569893d5ccc..c39962fa07f 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -97,7 +97,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index 4860228c7de..a0642d469aa 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -180,7 +180,7 @@ jobs: --with-sysroot=sysroot --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 CC=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-gcc-${{ inputs.gcc-major-version }} CXX=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-g++-${{ inputs.gcc-major-version }} ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 121416106f2..791b53a3f04 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -144,7 +144,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 32c5d43acbc..484e616fad7 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -111,7 +111,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index b7b2a6b5e17..5a9fdc57c74 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -69,20 +69,20 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], # Debug prefix mapping if supported by compiler DEBUG_PREFIX_CFLAGS= - UTIL_ARG_WITH(NAME: debug-info-level, TYPE: string, + UTIL_ARG_WITH(NAME: native-debug-symbols-level, TYPE: string, DEFAULT: "", - RESULT: DEBUG_INFO_LEVEL, - DESC: [Sets the debug info level, when debug info generation is enabled (GCC and Clang only)], - DEFAULT_DESC: [default]) - AC_SUBST(DEBUG_INFO_LEVEL) + RESULT: DEBUG_SYMBOLS_LEVEL, + DESC: [set the native debug symbol level (GCC and Clang only)], + DEFAULT_DESC: [toolchain default]) + AC_SUBST(DEBUG_SYMBOLS_LEVEL) if test "x${TOOLCHAIN_TYPE}" = xgcc || \ test "x${TOOLCHAIN_TYPE}" = xclang; then - DEBUG_INFO_LEVEL_FLAGS="-g" - if test "x${DEBUG_INFO_LEVEL}" != "x"; then - DEBUG_INFO_LEVEL_FLAGS="-g${DEBUG_INFO_LEVEL}" - FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_INFO_LEVEL_FLAGS}], - IF_FALSE: AC_MSG_ERROR("Debug info level ${DEBUG_INFO_LEVEL} is not supported")) + DEBUG_SYMBOLS_LEVEL_FLAGS="-g" + if test "x${DEBUG_SYMBOLS_LEVEL}" != "x"; then + DEBUG_SYMBOLS_LEVEL_FLAGS="-g${DEBUG_SYMBOLS_LEVEL}" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_SYMBOLS_LEVEL_FLAGS}], + IF_FALSE: AC_MSG_ERROR("Debug info level ${DEBUG_SYMBOLS_LEVEL} is not supported")) fi fi @@ -111,8 +111,8 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], fi # Debug info level should follow the debug format to be effective. - CFLAGS_DEBUG_SYMBOLS="-gdwarf-4 ${DEBUG_INFO_LEVEL_FLAGS}" - ASFLAGS_DEBUG_SYMBOLS="${DEBUG_INFO_LEVEL_FLAGS}" + CFLAGS_DEBUG_SYMBOLS="-gdwarf-4 ${DEBUG_SYMBOLS_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_SYMBOLS_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xclang; then if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then # Check if compiler supports -fdebug-prefix-map. If so, use that to make @@ -132,8 +132,8 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], IF_FALSE: [GDWARF_FLAGS=""]) # Debug info level should follow the debug format to be effective. - CFLAGS_DEBUG_SYMBOLS="${GDWARF_FLAGS} ${DEBUG_INFO_LEVEL_FLAGS}" - ASFLAGS_DEBUG_SYMBOLS="${DEBUG_INFO_LEVEL_FLAGS}" + CFLAGS_DEBUG_SYMBOLS="${GDWARF_FLAGS} ${DEBUG_SYMBOLS_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_SYMBOLS_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then CFLAGS_DEBUG_SYMBOLS="-Z7" fi diff --git a/make/langtools/tools/propertiesparser/gen/ClassGenerator.java b/make/langtools/tools/propertiesparser/gen/ClassGenerator.java index 247537b4676..14e8c4fb00a 100644 --- a/make/langtools/tools/propertiesparser/gen/ClassGenerator.java +++ b/make/langtools/tools/propertiesparser/gen/ClassGenerator.java @@ -286,7 +286,7 @@ public class ClassGenerator { diagnosticFlags.isEmpty() ? StubKind.DIAGNOSTIC_FLAGS_EMPTY.format() : StubKind.DIAGNOSTIC_FLAGS_NON_EMPTY.format(diagnosticFlags), - StubKind.LINT_CATEGORY.format("\"" + lintCategory + "\""), + StubKind.LINT_CATEGORY.format(toLintFieldName(lintCategory)), "\"" + keyParts[0] + "\"", "\"" + Stream.of(keyParts).skip(2).collect(Collectors.joining(".")) + "\"", javadoc); @@ -314,7 +314,7 @@ public class ClassGenerator { diagnosticFlags.isEmpty() ? StubKind.DIAGNOSTIC_FLAGS_EMPTY.format() : StubKind.DIAGNOSTIC_FLAGS_NON_EMPTY.format(diagnosticFlags), - StubKind.LINT_CATEGORY.format("\"" + lintCategory + "\""), + StubKind.LINT_CATEGORY.format(toLintFieldName(lintCategory)), "\"" + keyParts[0] + "\"", "\"" + Stream.of(keyParts).skip(2).collect(Collectors.joining(".")) + "\"", argNames.stream().collect(Collectors.joining(", "))); @@ -329,6 +329,11 @@ public class ClassGenerator { } } + String toLintFieldName(String lintCategory) { + return lintCategory.toUpperCase() + .replaceAll("-", "_"); + } + /** * Form the name of a factory method/field given a resource key. */ diff --git a/make/langtools/tools/propertiesparser/resources/templates.properties b/make/langtools/tools/propertiesparser/resources/templates.properties index 81a9be2552c..f8ff07a878f 100644 --- a/make/langtools/tools/propertiesparser/resources/templates.properties +++ b/make/langtools/tools/propertiesparser/resources/templates.properties @@ -87,7 +87,7 @@ suppress.warnings=\ @SuppressWarnings("rawtypes")\n lint.category=\ - LintCategory.get({0}).get() + LintCategory.{0} diagnostic.flags.empty=\ EnumSet.noneOf(DiagnosticFlag.class) diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 22f19c4f5ea..36f0864da0b 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -167,11 +167,6 @@ void VM_Version::common_initialize() { (unaligned_scalar.value() == MISALIGNED_SCALAR_FAST)); } - if (FLAG_IS_DEFAULT(AlignVector)) { - FLAG_SET_DEFAULT(AlignVector, - unaligned_vector.value() != MISALIGNED_VECTOR_FAST); - } - #ifdef __riscv_ztso // Hotspot is compiled with TSO support, it will only run on hardware which // supports Ztso @@ -242,6 +237,11 @@ void VM_Version::c2_initialize() { } } + if (FLAG_IS_DEFAULT(AlignVector)) { + FLAG_SET_DEFAULT(AlignVector, + unaligned_vector.value() != MISALIGNED_VECTOR_FAST); + } + // NOTE: Make sure codes dependent on UseRVV are put after MaxVectorSize initialize, // as there are extra checks inside it which could disable UseRVV // in some situations. diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index be7deb884ce..7f7bb2c4c7f 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -6086,7 +6086,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, vpbroadcastd(xtmp, xtmp, Assembler::AVX_512bit); subptr(count, 16 << shift); - jccb(Assembler::less, L_check_fill_32_bytes); + jcc(Assembler::less, L_check_fill_32_bytes); align(16); BIND(L_fill_64_bytes_loop_avx3); diff --git a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp index 25ffd0b8078..28159ae4801 100644 --- a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp @@ -66,9 +66,6 @@ #endif // open(2) flags -#ifndef O_CLOEXEC -#define O_CLOEXEC 02000000 -#endif #ifndef O_TMPFILE #define O_TMPFILE (020000000 | O_DIRECTORY) #endif diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 88e5e9b582a..6a2a3974a16 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4878,31 +4878,8 @@ int os::open(const char *path, int oflag, int mode) { // All file descriptors that are opened in the Java process and not // specifically destined for a subprocess should have the close-on-exec // flag set. If we don't set it, then careless 3rd party native code - // might fork and exec without closing all appropriate file descriptors, - // and this in turn might: - // - // - cause end-of-file to fail to be detected on some file - // descriptors, resulting in mysterious hangs, or - // - // - might cause an fopen in the subprocess to fail on a system - // suffering from bug 1085341. - // - // (Yes, the default setting of the close-on-exec flag is a Unix - // design flaw) - // - // See: - // 1085341: 32-bit stdio routines should support file descriptors >255 - // 4843136: (process) pipe file descriptor from Runtime.exec not being closed - // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9 - // - // Modern Linux kernels (after 2.6.23 2007) support O_CLOEXEC with open(). - // O_CLOEXEC is preferable to using FD_CLOEXEC on an open file descriptor - // because it saves a system call and removes a small window where the flag - // is unset. On ancient Linux kernels the O_CLOEXEC flag will be ignored - // and we fall back to using FD_CLOEXEC (see below). -#ifdef O_CLOEXEC + // might fork and exec without closing all appropriate file descriptors. oflag |= O_CLOEXEC; -#endif int fd = ::open(path, oflag, mode); if (fd == -1) return -1; @@ -4925,21 +4902,6 @@ int os::open(const char *path, int oflag, int mode) { } } -#ifdef FD_CLOEXEC - // Validate that the use of the O_CLOEXEC flag on open above worked. - // With recent kernels, we will perform this check exactly once. - static sig_atomic_t O_CLOEXEC_is_known_to_work = 0; - if (!O_CLOEXEC_is_known_to_work) { - int flags = ::fcntl(fd, F_GETFD); - if (flags != -1) { - if ((flags & FD_CLOEXEC) != 0) - O_CLOEXEC_is_known_to_work = 1; - else - ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC); - } - } -#endif - return fd; } diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 4cae7d359e4..5412e2bc92d 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -458,12 +458,10 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) { warning("Failed mmap to file. (%s)", os::strerror(errno)); return nullptr; } - if (base != nullptr && addr != base) { - if (!os::release_memory(addr, size)) { - warning("Could not release memory on unsuccessful file mapping"); - } - return nullptr; - } + + // The requested address should be the same as the returned address when using MAP_FIXED + // as per POSIX. + assert(base == nullptr || addr == base, "base should equal addr when using MAP_FIXED"); return addr; } diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 39bfc72a486..08a19270943 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2001, 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 @@ -946,7 +946,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size if (result == -1 ) break; if (!os::write(fd, &zero_int, 1)) { if (errno == ENOSPC) { - warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename); + warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename); } result = OS_ERR; break; diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 85babea5603..203e13a46ac 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.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 @@ -951,6 +951,32 @@ struct enum_sigcode_desc_t { const char* s_desc; }; +#if defined(LINUX) +// Additional kernel si_code definitions that are only exported by +// more recent glibc distributions, so we have to hard-code the values. +#ifndef BUS_MCEERR_AR // glibc 2.17 +#define BUS_MCEERR_AR 4 +#define BUS_MCEERR_AO 5 +#endif + +#ifndef SEGV_PKUERR // glibc 2.27 +#define SEGV_PKUERR 4 +#endif + +#ifndef SYS_SECCOMP // glibc 2.28 +#define SYS_SECCOMP 1 +#endif + +#ifndef TRAP_BRANCH // glibc 2.30 +#define TRAP_BRANCH 3 +#endif + +#ifndef TRAP_HWBKPT // not glibc version specific - gdb related +#define TRAP_HWBKPT 4 +#endif + +#endif // LINUX + static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t* out) { const struct { @@ -976,6 +1002,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for mapped object." }, #if defined(LINUX) { SIGSEGV, SEGV_BNDERR, "SEGV_BNDERR", "Failed address bound checks." }, + { SIGSEGV, SEGV_PKUERR, "SEGV_PKUERR", "Protection key checking failure." }, #endif #if defined(AIX) // no explanation found what keyerr would be @@ -984,8 +1011,18 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." }, { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." }, { SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." }, +#if defined(LINUX) + { SIGBUS, BUS_MCEERR_AR,"BUS_MCEERR_AR","Hardware memory error consumed on a machine check: action required." }, + { SIGBUS, BUS_MCEERR_AO,"BUS_MCEERR_AO","Hardware memory error detected in process but not consumed: action optional." }, + + { SIGSYS, SYS_SECCOMP, "SYS_SECCOMP", "Secure computing (seccomp) filter failure." }, +#endif { SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint." }, { SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap." }, +#if defined(LINUX) + { SIGTRAP, TRAP_BRANCH, "TRAP_BRANCH", "Process taken branch trap." }, + { SIGTRAP, TRAP_HWBKPT, "TRAP_HWBKPT", "Hardware breakpoint/watchpoint." }, +#endif { SIGCHLD, CLD_EXITED, "CLD_EXITED", "Child has exited." }, { SIGCHLD, CLD_KILLED, "CLD_KILLED", "Child has terminated abnormally and did not create a core file." }, { SIGCHLD, CLD_DUMPED, "CLD_DUMPED", "Child has terminated abnormally and created a core file." }, @@ -993,6 +1030,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGCHLD, CLD_STOPPED, "CLD_STOPPED", "Child has stopped." }, { SIGCHLD, CLD_CONTINUED,"CLD_CONTINUED","Stopped child has continued." }, #ifdef SIGPOLL + { SIGPOLL, POLL_IN, "POLL_IN", "Data input available." }, { SIGPOLL, POLL_OUT, "POLL_OUT", "Output buffers available." }, { SIGPOLL, POLL_MSG, "POLL_MSG", "Input message available." }, { SIGPOLL, POLL_ERR, "POLL_ERR", "I/O error." }, diff --git a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp index 1f9917acae7..c741335b5f0 100644 --- a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp +++ b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,7 @@ #ifndef OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP #define OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void *loc, intx interval) { #if !defined(USE_XLC_BUILTINS) diff --git a/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp b/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp index 1bcbdcaf90d..185f9b54144 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,8 +27,7 @@ #ifndef OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP #define OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0) diff --git a/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp b/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp index 52cc405e211..464740920a1 100644 --- a/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp +++ b/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * 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 +25,7 @@ #ifndef OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP #define OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); diff --git a/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp b/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp index 220d6a08f68..5edf3744719 100644 --- a/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp +++ b/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP #define OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void* loc, intx interval) { } diff --git a/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp b/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp index 168a680a404..580e9f4ffa2 100644 --- a/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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. * @@ -26,8 +26,7 @@ #ifndef OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP #define OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0) diff --git a/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp b/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp index dfbab55d9b9..a536c17fbe3 100644 --- a/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp +++ b/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, 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 @@ -25,7 +25,7 @@ #ifndef OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP #define OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { #if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_5TE__) diff --git a/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp b/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp index 12c65e6bf00..c33624a07de 100644 --- a/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp +++ b/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,7 @@ #ifndef OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP #define OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void *loc, intx interval) { __asm__ __volatile__ ( diff --git a/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp b/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp index a2dd79544c1..c17054e8a0c 100644 --- a/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp +++ b/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP #define OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0 && UseZicbop) { diff --git a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp index 9982b5860a8..749f6bec032 100644 --- a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp +++ b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp @@ -30,7 +30,7 @@ #include "code/nativeInst.hpp" #include "code/vtableStubs.hpp" #include "compiler/disassembler.hpp" -#include "cppstdlib/cstdlib.h" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" diff --git a/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp b/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp index ee55d01886f..56038714a9a 100644 --- a/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp +++ b/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, 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. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP #define OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void* loc, intx interval) { // No prefetch instructions on z/Architecture -> implement trivially. diff --git a/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp b/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp index bb4302e1ddb..aadd21bca40 100644 --- a/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp +++ b/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * 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 +25,7 @@ #ifndef OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP #define OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); diff --git a/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp b/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp index a510fa87834..3a34c311acd 100644 --- a/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp +++ b/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP #define OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void* loc, intx interval) { } diff --git a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp index df301ade92d..a360ee342be 100644 --- a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Microsoft Corporation. All rights reserved. + * Copyright (c) 2020, 2026, Microsoft 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 +25,7 @@ #ifndef OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP #define OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { } diff --git a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp index 996625cb0ad..645fbe99a22 100644 --- a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp +++ b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, 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 @@ -25,7 +25,7 @@ #ifndef OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP #define OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) {} inline void Prefetch::write(void *loc, intx interval) {} diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index c4bb26f6fb1..93145940955 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.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 @@ -116,6 +116,10 @@ bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* c return false; } } else if (resolved_class->is_objArray_klass()) { + if (CDSConfig::is_dumping_dynamic_archive()) { + // This is difficult to handle. See JDK-8374639 + return false; + } Klass* elem = ObjArrayKlass::cast(resolved_class)->bottom_klass(); if (elem->is_instance_klass()) { return is_class_resolution_deterministic(cp_holder, InstanceKlass::cast(elem)); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 9c67b2e0e8d..e73b980614a 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -696,7 +696,7 @@ template void AOTMappedHeapWriter::relocate_field_in_buffer(T* fiel // We use zero-based, 0-shift encoding, so the narrowOop is just the lower // 32 bits of request_referent intptr_t addr = cast_from_oop(request_referent); - *((narrowOop*)field_addr_in_buffer) = checked_cast(addr); + *((narrowOop*)field_addr_in_buffer) = CompressedOops::narrow_oop_cast(addr); } else { store_requested_oop_in_buffer(field_addr_in_buffer, request_referent); } diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 3824a2be3e2..79d789e0c70 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.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 @@ -2159,7 +2159,6 @@ void AOTMetaspace::initialize_shared_spaces() { intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data(); ReadClosure rc(&buffer, (intptr_t)SharedBaseAddress); DynamicArchive::serialize(&rc); - DynamicArchive::setup_array_klasses(); } LogStreamHandle(Info, aot) lsh; diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 3c7f25a950b..6bbefea5cd9 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.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 @@ -984,8 +984,6 @@ void ArchiveBuilder::make_klasses_shareable() { #undef STATS_FORMAT #undef STATS_PARAMS - - DynamicArchive::make_array_klasses_shareable(); } void ArchiveBuilder::make_training_data_shareable() { diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 8fae8dabf8c..d39cf3775e4 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.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 @@ -48,6 +48,7 @@ #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" +#include "oops/array.hpp" #include "oops/klass.inline.hpp" #include "runtime/arguments.hpp" #include "runtime/os.hpp" @@ -95,7 +96,6 @@ public: void sort_methods(InstanceKlass* ik) const; void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; void write_archive(char* serialized_data, AOTClassLocationConfig* cl_config); - void gather_array_klasses(); public: // Do this before and after the archive dump to see if any corruption @@ -132,7 +132,6 @@ public: init_header(); gather_source_objs(); - gather_array_klasses(); reserve_buffer(); log_info(cds, dynamic)("Copying %d klasses and %d symbols", @@ -159,7 +158,6 @@ public: ArchiveBuilder::OtherROAllocMark mark; SystemDictionaryShared::write_to_archive(false); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); - DynamicArchive::dump_array_klasses(); serialized_data = ro_region()->top(); WriteClosure wc(ro_region()); @@ -175,8 +173,6 @@ public: write_archive(serialized_data, cl_config); release_header(); - DynamicArchive::post_dump(); - post_dump(); verify_universe("After CDS dynamic dump"); @@ -185,30 +181,6 @@ public: virtual void iterate_roots(MetaspaceClosure* it) { AOTArtifactFinder::all_cached_classes_do(it); SystemDictionaryShared::dumptime_classes_do(it); - iterate_primitive_array_klasses(it); - } - - void iterate_primitive_array_klasses(MetaspaceClosure* it) { - for (int i = T_BOOLEAN; i <= T_LONG; i++) { - assert(is_java_primitive((BasicType)i), "sanity"); - Klass* k = Universe::typeArrayKlass((BasicType)i); // this give you "[I", etc - assert(AOTMetaspace::in_aot_cache_static_region((void*)k), - "one-dimensional primitive array should be in static archive"); - ArrayKlass* ak = ArrayKlass::cast(k); - while (ak != nullptr && ak->in_aot_cache()) { - Klass* next_k = ak->array_klass_or_null(); - if (next_k != nullptr) { - ak = ArrayKlass::cast(next_k); - } else { - ak = nullptr; - } - } - if (ak != nullptr) { - assert(ak->dimension() > 1, "sanity"); - // this is the lowest dimension that's not in the static archive - it->push(&ak); - } - } } }; @@ -367,26 +339,6 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio log_info(cds, dynamic)("%d klasses; %d symbols", klasses()->length(), symbols()->length()); } -void DynamicArchiveBuilder::gather_array_klasses() { - for (int i = 0; i < klasses()->length(); i++) { - if (klasses()->at(i)->is_objArray_klass()) { - ObjArrayKlass* oak = ObjArrayKlass::cast(klasses()->at(i)); - Klass* elem = oak->element_klass(); - if (AOTMetaspace::in_aot_cache_static_region(elem)) { - // Only capture the array klass whose element_klass is in the static archive. - // During run time, setup (see DynamicArchive::setup_array_klasses()) is needed - // so that the element_klass can find its array klasses from the dynamic archive. - DynamicArchive::append_array_klass(oak); - } else { - // The element_klass and its array klasses are in the same archive. - assert(!AOTMetaspace::in_aot_cache_static_region(oak), - "we should not gather klasses that are already in the static archive"); - } - } - } - log_debug(aot)("Total array klasses gathered for dynamic archive: %d", DynamicArchive::num_array_klasses()); -} - class VM_PopulateDynamicDumpSharedSpace: public VM_Heap_Sync_Operation { DynamicArchiveBuilder _builder; public: @@ -403,76 +355,9 @@ public: } }; -// _array_klasses and _dynamic_archive_array_klasses only hold the array klasses -// which have element klass in the static archive. -GrowableArray* DynamicArchive::_array_klasses = nullptr; -Array* DynamicArchive::_dynamic_archive_array_klasses = nullptr; - void DynamicArchive::serialize(SerializeClosure* soc) { SymbolTable::serialize_shared_table_header(soc, false); SystemDictionaryShared::serialize_dictionary_headers(soc, false); - soc->do_ptr(&_dynamic_archive_array_klasses); -} - -void DynamicArchive::append_array_klass(ObjArrayKlass* ak) { - if (_array_klasses == nullptr) { - _array_klasses = new (mtClassShared) GrowableArray(50, mtClassShared); - } - _array_klasses->append(ak); -} - -void DynamicArchive::dump_array_klasses() { - assert(CDSConfig::is_dumping_dynamic_archive(), "sanity"); - if (_array_klasses != nullptr) { - ArchiveBuilder* builder = ArchiveBuilder::current(); - int num_array_klasses = _array_klasses->length(); - _dynamic_archive_array_klasses = - ArchiveBuilder::new_ro_array(num_array_klasses); - for (int i = 0; i < num_array_klasses; i++) { - builder->write_pointer_in_buffer(_dynamic_archive_array_klasses->adr_at(i), _array_klasses->at(i)); - } - } -} - -void DynamicArchive::setup_array_klasses() { - if (_dynamic_archive_array_klasses != nullptr) { - for (int i = 0; i < _dynamic_archive_array_klasses->length(); i++) { - ObjArrayKlass* oak = _dynamic_archive_array_klasses->at(i); - Klass* elm = oak->element_klass(); - assert(AOTMetaspace::in_aot_cache_static_region((void*)elm), "must be"); - - if (elm->is_instance_klass()) { - assert(InstanceKlass::cast(elm)->array_klasses() == nullptr, "must be"); - InstanceKlass::cast(elm)->set_array_klasses(oak); - } else { - assert(elm->is_array_klass(), "sanity"); - assert(ArrayKlass::cast(elm)->higher_dimension() == nullptr, "must be"); - ArrayKlass::cast(elm)->set_higher_dimension(oak); - } - } - log_debug(aot)("Total array klasses read from dynamic archive: %d", _dynamic_archive_array_klasses->length()); - } -} - -void DynamicArchive::make_array_klasses_shareable() { - if (_array_klasses != nullptr) { - int num_array_klasses = _array_klasses->length(); - for (int i = 0; i < num_array_klasses; i++) { - ObjArrayKlass* k = ArchiveBuilder::current()->get_buffered_addr(_array_klasses->at(i)); - k->remove_unshareable_info(); - } - } -} - -void DynamicArchive::post_dump() { - if (_array_klasses != nullptr) { - delete _array_klasses; - _array_klasses = nullptr; - } -} - -int DynamicArchive::num_array_klasses() { - return _array_klasses != nullptr ? _array_klasses->length() : 0; } void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAPS) { diff --git a/src/hotspot/share/cds/dynamicArchive.hpp b/src/hotspot/share/cds/dynamicArchive.hpp index c42c4b7dfde..63a48e252ee 100644 --- a/src/hotspot/share/cds/dynamicArchive.hpp +++ b/src/hotspot/share/cds/dynamicArchive.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,8 @@ #define SHARE_CDS_DYNAMICARCHIVE_HPP #include "cds/filemap.hpp" -#include "classfile/compactHashtable.hpp" #include "memory/allStatic.hpp" -#include "memory/memRegion.hpp" -#include "oops/array.hpp" -#include "oops/oop.hpp" #include "utilities/exceptions.hpp" -#include "utilities/growableArray.hpp" #include "utilities/macros.hpp" #if INCLUDE_CDS @@ -59,22 +54,13 @@ public: }; class DynamicArchive : AllStatic { -private: - static GrowableArray* _array_klasses; - static Array* _dynamic_archive_array_klasses; public: static void dump_for_jcmd(const char* archive_name, TRAPS); static void dump_at_exit(JavaThread* current); static void dump_impl(bool jcmd_request, const char* archive_name, TRAPS); static bool is_mapped() { return FileMapInfo::dynamic_info() != nullptr; } static bool validate(FileMapInfo* dynamic_info); - static void dump_array_klasses(); - static void setup_array_klasses(); - static void append_array_klass(ObjArrayKlass* oak); static void serialize(SerializeClosure* soc); - static void make_array_klasses_shareable(); - static void post_dump(); - static int num_array_klasses(); }; #endif // INCLUDE_CDS #endif // SHARE_CDS_DYNAMICARCHIVE_HPP diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 77a94c8afa5..dd70d7b49ab 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.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 @@ -4297,10 +4297,6 @@ int jdk_internal_foreign_abi_NativeEntryPoint::_downcall_stub_address_offset; macro(_method_type_offset, k, "methodType", java_lang_invoke_MethodType_signature, false); \ macro(_downcall_stub_address_offset, k, "downcallStubAddress", long_signature, false); -bool jdk_internal_foreign_abi_NativeEntryPoint::is_instance(oop obj) { - return obj != nullptr && is_subclass(obj->klass()); -} - void jdk_internal_foreign_abi_NativeEntryPoint::compute_offsets() { InstanceKlass* k = vmClasses::NativeEntryPoint_klass(); NEP_FIELDS_DO(FIELD_COMPUTE_OFFSET); @@ -4337,10 +4333,6 @@ int jdk_internal_foreign_abi_ABIDescriptor::_scratch2_offset; macro(_scratch1_offset, k, "scratch1", jdk_internal_foreign_abi_VMStorage_signature, false); \ macro(_scratch2_offset, k, "scratch2", jdk_internal_foreign_abi_VMStorage_signature, false); -bool jdk_internal_foreign_abi_ABIDescriptor::is_instance(oop obj) { - return obj != nullptr && is_subclass(obj->klass()); -} - void jdk_internal_foreign_abi_ABIDescriptor::compute_offsets() { InstanceKlass* k = vmClasses::ABIDescriptor_klass(); ABIDescriptor_FIELDS_DO(FIELD_COMPUTE_OFFSET); diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 33dc912404c..a8562a345c8 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.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 @@ -1179,13 +1179,6 @@ class jdk_internal_foreign_abi_NativeEntryPoint: AllStatic { static oop method_type(oop entry); static jlong downcall_stub_address(oop entry); - // Testers - static bool is_subclass(Klass* klass) { - return vmClasses::NativeEntryPoint_klass() != nullptr && - klass->is_subclass_of(vmClasses::NativeEntryPoint_klass()); - } - static bool is_instance(oop obj); - // Accessors for code generation: static int method_type_offset_in_bytes() { return _method_type_offset; } static int downcall_stub_address_offset_in_bytes() { return _downcall_stub_address_offset; } @@ -1216,13 +1209,6 @@ class jdk_internal_foreign_abi_ABIDescriptor: AllStatic { static jint shadowSpace(oop entry); static oop scratch1(oop entry); static oop scratch2(oop entry); - - // Testers - static bool is_subclass(Klass* klass) { - return vmClasses::ABIDescriptor_klass() != nullptr && - klass->is_subclass_of(vmClasses::ABIDescriptor_klass()); - } - static bool is_instance(oop obj); }; class jdk_internal_foreign_abi_VMStorage: AllStatic { diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 0314c5227d2..f51c068f1e7 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.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 @@ -1371,6 +1371,7 @@ void AOTCodeAddressTable::init_extrs() { SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); #endif #if INCLUDE_ZGC + SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); #if defined(AMD64) SET_ADDRESS(_extrs, &ZPointerLoadShift); diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index e5ae673ef0c..59ab69b2427 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -62,8 +62,6 @@ jint EpsilonHeap::initialize() { // Enable monitoring _monitoring_support = new EpsilonMonitoringSupport(this); - _last_counter_update = 0; - _last_heap_print = 0; // Install barrier set BarrierSet::set_barrier_set(new EpsilonBarrierSet()); @@ -77,6 +75,7 @@ jint EpsilonHeap::initialize() { void EpsilonHeap::initialize_serviceability() { _pool = new EpsilonMemoryPool(this); _memory_manager.add_pool(_pool); + _monitoring_support->mark_ready(); } GrowableArray EpsilonHeap::memory_managers() { @@ -101,7 +100,7 @@ EpsilonHeap* EpsilonHeap::heap() { return named_heap(CollectedHeap::Epsilon); } -HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) { +HeapWord* EpsilonHeap::allocate_work(size_t size) { assert(is_object_aligned(size), "Allocation size should be aligned: %zu", size); HeapWord* res = nullptr; @@ -151,19 +150,23 @@ HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) { size_t used = _space->used(); - // Allocation successful, update counters - if (verbose) { - size_t last = _last_counter_update; - if ((used - last >= _step_counter_update) && AtomicAccess::cmpxchg(&_last_counter_update, last, used) == last) { + // Allocation successful, update counters and print status. + // At this point, some diagnostic subsystems might not yet be initialized. + // We pretend the printout happened either way. This keeps allocation path + // from obsessively checking the subsystems' status on every allocation. + size_t last_counter = _last_counter_update.load_relaxed(); + if ((used - last_counter >= _step_counter_update) && + _last_counter_update.compare_set(last_counter, used)) { + if (_monitoring_support->is_ready()) { _monitoring_support->update_counters(); } } - // ...and print the occupancy line, if needed - if (verbose) { - size_t last = _last_heap_print; - if ((used - last >= _step_heap_print) && AtomicAccess::cmpxchg(&_last_heap_print, last, used) == last) { - print_heap_info(used); + size_t last_heap = _last_heap_print.load_relaxed(); + if ((used - last_heap >= _step_heap_print) && + _last_heap_print.compare_set(last_heap, used)) { + print_heap_info(used); + if (Metaspace::initialized()) { print_metaspace_info(); } } @@ -265,8 +268,7 @@ HeapWord* EpsilonHeap::mem_allocate(size_t size) { } HeapWord* EpsilonHeap::allocate_loaded_archive_space(size_t size) { - // Cannot use verbose=true because Metaspace is not initialized - return allocate_work(size, /* verbose = */false); + return allocate_work(size); } void EpsilonHeap::collect(GCCause::Cause cause) { diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp index 4f812bde8b3..8d7aa7960fd 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp @@ -31,6 +31,7 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/space.hpp" #include "memory/virtualspace.hpp" +#include "runtime/atomic.hpp" #include "services/memoryManager.hpp" class EpsilonHeap : public CollectedHeap { @@ -45,8 +46,8 @@ private: size_t _step_counter_update; size_t _step_heap_print; int64_t _decay_time_ns; - volatile size_t _last_counter_update; - volatile size_t _last_heap_print; + Atomic _last_counter_update; + Atomic _last_heap_print; void print_tracing_info() const override; void stop() override {}; @@ -83,7 +84,7 @@ public: bool requires_barriers(stackChunkOop obj) const override { return false; } // Allocation - HeapWord* allocate_work(size_t size, bool verbose = true); + HeapWord* allocate_work(size_t size); HeapWord* mem_allocate(size_t size) override; HeapWord* allocate_new_tlab(size_t min_size, size_t requested_size, diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp index 51d0a8356d2..213fc18b8ff 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp @@ -99,6 +99,7 @@ EpsilonMonitoringSupport::EpsilonMonitoringSupport(EpsilonHeap* heap) { } void EpsilonMonitoringSupport::update_counters() { + assert(is_ready(), "Must be ready"); MemoryService::track_memory_usage(); if (UsePerfData) { @@ -110,3 +111,11 @@ void EpsilonMonitoringSupport::update_counters() { MetaspaceCounters::update_performance_counters(); } } + +bool EpsilonMonitoringSupport::is_ready() { + return _ready.load_acquire(); +} + +void EpsilonMonitoringSupport::mark_ready() { + _ready.release_store(true); +} diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp index 67a60d92778..9dc52c2a659 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_EPSILON_EPSILONMONITORINGSUPPORT_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class EpsilonGenerationCounters; class EpsilonSpaceCounters; @@ -35,9 +36,12 @@ class EpsilonMonitoringSupport : public CHeapObj { private: EpsilonGenerationCounters* _heap_counters; EpsilonSpaceCounters* _space_counters; + Atomic _ready; public: EpsilonMonitoringSupport(EpsilonHeap* heap); + bool is_ready(); + void mark_ready(); void update_counters(); }; diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index 2be0e008c22..58e76cdd43a 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -36,6 +36,7 @@ #include "gc/shared/fullGCForwarding.hpp" #include "gc/shared/gcArguments.hpp" #include "gc/shared/workerPolicy.hpp" +#include "runtime/flags/jvmFlagLimit.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" @@ -190,7 +191,8 @@ void G1Arguments::initialize() { } FLAG_SET_DEFAULT(G1ConcRefinementThreads, 0); } else if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) { - FLAG_SET_ERGO(G1ConcRefinementThreads, ParallelGCThreads); + const JVMTypedFlagLimit* conc_refinement_threads_limits = JVMFlagLimit::get_range_at(FLAG_MEMBER_ENUM(G1ConcRefinementThreads))->cast(); + FLAG_SET_ERGO(G1ConcRefinementThreads, MIN2(ParallelGCThreads, conc_refinement_threads_limits->max())); } if (FLAG_IS_DEFAULT(ConcGCThreads) || ConcGCThreads == 0) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 061241c24e2..2ad5a26d5e6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.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 @@ -686,7 +686,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { // the check before we do the actual allocation. The reason for doing it // before the allocation is that we avoid having to keep track of the newly // allocated memory while we do a GC. - if (policy()->need_to_start_conc_mark("concurrent humongous allocation", + // Only try that if we can actually perform a GC. + if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation", word_size)) { try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this)); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index d875d9c8b8a..ec5d2393d8c 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -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 @@ -46,7 +46,7 @@ #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/prefetch.hpp" +#include "runtime/prefetch.inline.hpp" #include "runtime/threads.hpp" #include "runtime/threadSMR.hpp" #include "utilities/bitMap.inline.hpp" diff --git a/src/hotspot/share/gc/serial/cardTableRS.cpp b/src/hotspot/share/gc/serial/cardTableRS.cpp index 80985424a62..a53ab066387 100644 --- a/src/hotspot/share/gc/serial/cardTableRS.cpp +++ b/src/hotspot/share/gc/serial/cardTableRS.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 @@ -28,6 +28,7 @@ #include "gc/serial/serialHeap.inline.hpp" #include "gc/shared/space.hpp" #include "memory/iterator.inline.hpp" +#include "runtime/prefetch.inline.hpp" #include "utilities/align.hpp" void CardTableRS::scan_old_to_young_refs(TenuredGeneration* tg, HeapWord* saved_top) { diff --git a/src/hotspot/share/gc/serial/generation.hpp b/src/hotspot/share/gc/serial/generation.hpp index 8c9da3b42b7..ddfd4028a7d 100644 --- a/src/hotspot/share/gc/serial/generation.hpp +++ b/src/hotspot/share/gc/serial/generation.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 @@ -34,7 +34,6 @@ #include "memory/virtualspace.hpp" #include "runtime/mutex.hpp" #include "runtime/perfData.hpp" -#include "runtime/prefetch.inline.hpp" // A Generation models a heap area for similarly-aged objects. // It will contain one ore more spaces holding the actual objects. diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 08d730bf877..8eafdfdcc82 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -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 @@ -70,6 +70,7 @@ #include "runtime/init.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" +#include "runtime/prefetch.inline.hpp" #include "runtime/threads.hpp" #include "runtime/vmThread.hpp" #include "services/memoryManager.hpp" diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index cb5d6b5a886..ab94bae079a 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.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 @@ -33,6 +33,7 @@ #include "memory/universe.hpp" #include "oops/access.inline.hpp" #include "oops/method.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/threads.hpp" @@ -196,8 +197,8 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) { // Diagnostic option to force deoptimization 1 in 10 times. It is otherwise // a very rare event. if (DeoptimizeNMethodBarriersALot && !nm->is_osr_method()) { - static volatile uint32_t counter=0; - if (AtomicAccess::add(&counter, 1u) % 10 == 0) { + static Atomic counter{0}; + if (counter.add_then_fetch(1u) % 10 == 0) { may_enter = false; } } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.cpp b/src/hotspot/share/gc/shared/concurrentGCThread.cpp index ac281f82f8a..ed6c1b4d283 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.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 @@ -23,7 +23,7 @@ */ #include "gc/shared/concurrentGCThread.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/init.hpp" #include "runtime/jniHandles.hpp" #include "runtime/mutexLocker.hpp" @@ -48,7 +48,7 @@ void ConcurrentGCThread::run() { // Signal thread has terminated MonitorLocker ml(Terminator_lock); - AtomicAccess::release_store(&_has_terminated, true); + _has_terminated.release_store(true); ml.notify_all(); } @@ -57,21 +57,21 @@ void ConcurrentGCThread::stop() { assert(!has_terminated(), "Invalid state"); // Signal thread to terminate - AtomicAccess::release_store_fence(&_should_terminate, true); + _should_terminate.release_store_fence(true); stop_service(); // Wait for thread to terminate MonitorLocker ml(Terminator_lock); - while (!_has_terminated) { + while (!_has_terminated.load_relaxed()) { ml.wait(); } } bool ConcurrentGCThread::should_terminate() const { - return AtomicAccess::load_acquire(&_should_terminate); + return _should_terminate.load_acquire(); } bool ConcurrentGCThread::has_terminated() const { - return AtomicAccess::load_acquire(&_has_terminated); + return _has_terminated.load_acquire(); } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.hpp b/src/hotspot/share/gc/shared/concurrentGCThread.hpp index 630abeaeb9f..0c764546045 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.hpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,15 @@ #ifndef SHARE_GC_SHARED_CONCURRENTGCTHREAD_HPP #define SHARE_GC_SHARED_CONCURRENTGCTHREAD_HPP +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/nonJavaThread.hpp" #include "utilities/debug.hpp" class ConcurrentGCThread: public NamedThread { private: - volatile bool _should_terminate; - volatile bool _has_terminated; + Atomic _should_terminate; + Atomic _has_terminated; protected: void create_and_start(ThreadPriority prio = NearMaxPriority); diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp index 01d17b1117d..898588e6b06 100644 --- a/src/hotspot/share/gc/shared/gcLocker.cpp +++ b/src/hotspot/share/gc/shared/gcLocker.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 @@ -28,7 +28,7 @@ #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/safepoint.hpp" @@ -60,16 +60,13 @@ public: }; Monitor* GCLocker::_lock; -volatile bool GCLocker::_is_gc_request_pending; +Atomic GCLocker::_is_gc_request_pending{false}; -DEBUG_ONLY(uint64_t GCLocker::_verify_in_cr_count;) +DEBUG_ONLY(Atomic GCLocker::_verify_in_cr_count{0};) void GCLocker::initialize() { assert(JNICritical_lock != nullptr, "inv"); _lock = JNICritical_lock; - _is_gc_request_pending = false; - - DEBUG_ONLY(_verify_in_cr_count = 0;) } bool GCLocker::is_active() { @@ -84,11 +81,11 @@ bool GCLocker::is_active() { void GCLocker::block() { // _lock is held from the beginning of block() to the end of of unblock(). _lock->lock(); - assert(AtomicAccess::load(&_is_gc_request_pending) == false, "precondition"); + assert(_is_gc_request_pending.load_relaxed() == false, "precondition"); GCLockerTimingDebugLogger logger("Thread blocked to start GC."); - AtomicAccess::store(&_is_gc_request_pending, true); + _is_gc_request_pending.store_relaxed(true); // The _is_gc_request_pending and _jni_active_critical (inside // in_critical_atomic()) variables form a Dekker duality. On the GC side, the @@ -112,14 +109,14 @@ void GCLocker::block() { #ifdef ASSERT // Matching the storestore in GCLocker::exit. OrderAccess::loadload(); - assert(AtomicAccess::load(&_verify_in_cr_count) == 0, "inv"); + assert(_verify_in_cr_count.load_relaxed() == 0, "inv"); #endif } void GCLocker::unblock() { - assert(AtomicAccess::load(&_is_gc_request_pending) == true, "precondition"); + assert(_is_gc_request_pending.load_relaxed() == true, "precondition"); - AtomicAccess::store(&_is_gc_request_pending, false); + _is_gc_request_pending.store_relaxed(false); _lock->unlock(); } @@ -139,7 +136,7 @@ void GCLocker::enter_slow(JavaThread* current_thread) { // Same as fast path. OrderAccess::fence(); - if (!AtomicAccess::load(&_is_gc_request_pending)) { + if (!_is_gc_request_pending.load_relaxed()) { return; } diff --git a/src/hotspot/share/gc/shared/gcLocker.hpp b/src/hotspot/share/gc/shared/gcLocker.hpp index c0e28b66539..be3c0c3593e 100644 --- a/src/hotspot/share/gc/shared/gcLocker.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.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 @@ -27,6 +27,7 @@ #include "gc/shared/gcCause.hpp" #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" // GCLocker provides synchronization between the garbage collector (GC) and @@ -43,11 +44,11 @@ class GCLocker: public AllStatic { static Monitor* _lock; - static volatile bool _is_gc_request_pending; + static Atomic _is_gc_request_pending; #ifdef ASSERT // Debug-only: to track the number of java threads in critical-region. - static uint64_t _verify_in_cr_count; + static Atomic _verify_in_cr_count; #endif static void enter_slow(JavaThread* current_thread); diff --git a/src/hotspot/share/gc/shared/gcLocker.inline.hpp b/src/hotspot/share/gc/shared/gcLocker.inline.hpp index 050b9570280..135bd9eab62 100644 --- a/src/hotspot/share/gc/shared/gcLocker.inline.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.inline.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. * 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,13 +38,13 @@ void GCLocker::enter(JavaThread* current_thread) { // Matching the fence in GCLocker::block. OrderAccess::fence(); - if (AtomicAccess::load(&_is_gc_request_pending)) { + if (_is_gc_request_pending.load_relaxed()) { current_thread->exit_critical(); // slow-path enter_slow(current_thread); } - DEBUG_ONLY(AtomicAccess::add(&_verify_in_cr_count, (uint64_t)1);) + DEBUG_ONLY(_verify_in_cr_count.add_then_fetch(1u);) } else { current_thread->enter_critical(); } @@ -55,7 +55,7 @@ void GCLocker::exit(JavaThread* current_thread) { #ifdef ASSERT if (current_thread->in_last_critical()) { - AtomicAccess::add(&_verify_in_cr_count, (uint64_t)-1); + _verify_in_cr_count.sub_then_fetch(1u); // Matching the loadload in GCLocker::block. OrderAccess::storestore(); } diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index a1cc3ffa553..21e63f6fc32 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) { // then someone else has made such a claim and the deferred update has not // yet been processed and will include our change, so we don't need to do // anything further. - if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) { + if (_deferred_updates_next.compare_set(nullptr, this)) { // Successfully claimed. Push, with self-loop for end-of-list. Block* head = owner->_deferred_updates.load_relaxed(); while (true) { diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp index cc84c8c449d..58a3a2693ed 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.cpp +++ b/src/hotspot/share/gc/shared/pretouchTask.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 @@ -25,7 +25,7 @@ #include "gc/shared/gc_globals.hpp" #include "gc/shared/pretouchTask.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" @@ -52,11 +52,11 @@ size_t PretouchTask::chunk_size() { void PretouchTask::work(uint worker_id) { while (true) { - char* cur_start = AtomicAccess::load(&_cur_addr); + char* cur_start = _cur_addr.load_relaxed(); char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1)); if (cur_start >= cur_end) { break; - } else if (cur_start == AtomicAccess::cmpxchg(&_cur_addr, cur_start, cur_end)) { + } else if (_cur_addr.compare_set(cur_start, cur_end)) { os::pretouch_memory(cur_start, cur_end, _page_size); } // Else attempt to claim chunk failed, so try again. } diff --git a/src/hotspot/share/gc/shared/pretouchTask.hpp b/src/hotspot/share/gc/shared/pretouchTask.hpp index 7c66c8b717c..6355d61edf7 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.hpp +++ b/src/hotspot/share/gc/shared/pretouchTask.hpp @@ -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 @@ -26,9 +26,11 @@ #define SHARE_GC_SHARED_PRETOUCH_HPP #include "gc/shared/workerThread.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" class PretouchTask : public WorkerTask { - char* volatile _cur_addr; + Atomic _cur_addr; char* const _end_addr; size_t _page_size; size_t _chunk_size; diff --git a/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp b/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp index 83783b31ad9..834a3d04c00 100644 --- a/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp +++ b/src/hotspot/share/gc/shared/suspendibleThreadSet.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 @@ -30,7 +30,7 @@ uint SuspendibleThreadSet::_nthreads = 0; uint SuspendibleThreadSet::_nthreads_stopped = 0; -volatile bool SuspendibleThreadSet::_suspend_all = false; +Atomic SuspendibleThreadSet::_suspend_all{false}; double SuspendibleThreadSet::_suspend_all_start = 0.0; static Semaphore* _synchronize_wakeup = nullptr; @@ -96,7 +96,7 @@ void SuspendibleThreadSet::synchronize() { { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); assert(!should_yield(), "Only one at a time"); - AtomicAccess::store(&_suspend_all, true); + _suspend_all.store_relaxed(true); if (is_synchronized()) { return; } @@ -127,6 +127,6 @@ void SuspendibleThreadSet::desynchronize() { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); assert(should_yield(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); - AtomicAccess::store(&_suspend_all, false); + _suspend_all.store_relaxed(false); ml.notify_all(); } diff --git a/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp b/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp index 38568c015bc..72fca5302e7 100644 --- a/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp +++ b/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ #define SHARE_GC_SHARED_SUSPENDIBLETHREADSET_HPP #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" // A SuspendibleThreadSet is a set of threads that can be suspended. // A thread can join and later leave the set, and periodically yield. @@ -43,7 +43,7 @@ class SuspendibleThreadSet : public AllStatic { private: static uint _nthreads; static uint _nthreads_stopped; - static volatile bool _suspend_all; + static Atomic _suspend_all; static double _suspend_all_start; static bool is_synchronized(); @@ -59,7 +59,7 @@ private: public: // Returns true if an suspension is in progress. - static bool should_yield() { return AtomicAccess::load(&_suspend_all); } + static bool should_yield() { return _suspend_all.load_relaxed(); } // Suspends the current thread if a suspension is in progress. static void yield() { diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp index 3a751852ab6..4334773a4e9 100644 --- a/src/hotspot/share/gc/shared/taskqueue.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.hpp @@ -183,8 +183,8 @@ protected: _age.store_relaxed(new_age); } - Age cmpxchg_age(Age old_age, Age new_age) { - return _age.compare_exchange(old_age, new_age); + bool par_set_age(Age old_age, Age new_age) { + return _age.compare_set(old_age, new_age); } idx_t age_top_relaxed() const { @@ -345,7 +345,7 @@ protected: using TaskQueueSuper::age_relaxed; using TaskQueueSuper::set_age_relaxed; - using TaskQueueSuper::cmpxchg_age; + using TaskQueueSuper::par_set_age; using TaskQueueSuper::age_top_relaxed; using TaskQueueSuper::increment_index; diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index f115d94740b..55851495a5f 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -170,8 +170,7 @@ bool GenericTaskQueue::pop_local_slow(uint localBot, Age oldAge) { if (localBot == oldAge.top()) { // No competing pop_global has yet incremented "top"; we'll try to // install new_age, thus claiming the element. - Age tempAge = cmpxchg_age(oldAge, newAge); - if (tempAge == oldAge) { + if (par_set_age(oldAge, newAge)) { // We win. assert_not_underflow(localBot, age_top_relaxed()); TASKQUEUE_STATS_ONLY(stats.record_pop_slow()); @@ -283,12 +282,12 @@ typename GenericTaskQueue::PopResult GenericTaskQueue::pop_g idx_t new_top = increment_index(oldAge.top()); idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0); Age newAge(new_top, new_tag); - Age resAge = cmpxchg_age(oldAge, newAge); + bool result = par_set_age(oldAge, newAge); // Note that using "bottom" here might fail, since a pop_local might // have decremented it. assert_not_underflow(localBot, newAge.top()); - return resAge == oldAge ? PopResult::Success : PopResult::Contended; + return result ? PopResult::Success : PopResult::Contended; } inline int randomParkAndMiller(int *seed0) { diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 3a999da59dc..7a9404a195a 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.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 @@ -42,7 +42,7 @@ WorkerTaskDispatcher::WorkerTaskDispatcher() : void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint num_workers) { // No workers are allowed to read the state variables until they have been signaled. _task = task; - _not_finished = num_workers; + _not_finished.store_relaxed(num_workers); // Dispatch 'num_workers' number of tasks. _start_semaphore.signal(num_workers); @@ -51,9 +51,12 @@ void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint nu _end_semaphore.wait(); // No workers are allowed to read the state variables after the coordinator has been signaled. - assert(_not_finished == 0, "%d not finished workers?", _not_finished); +#ifdef ASSERT + uint not_finished = _not_finished.load_relaxed(); + assert(not_finished == 0, "%u not finished workers?", not_finished); +#endif // ASSERT _task = nullptr; - _started = 0; + _started.store_relaxed(0); } void WorkerTaskDispatcher::worker_run_task() { @@ -61,7 +64,7 @@ void WorkerTaskDispatcher::worker_run_task() { _start_semaphore.wait(); // Get and set worker id. - const uint worker_id = AtomicAccess::fetch_then_add(&_started, 1u); + const uint worker_id = _started.fetch_then_add(1u); WorkerThread::set_worker_id(worker_id); // Run task. @@ -70,7 +73,7 @@ void WorkerTaskDispatcher::worker_run_task() { // Mark that the worker is done with the task. // The worker is not allowed to read the state variables after this line. - const uint not_finished = AtomicAccess::sub(&_not_finished, 1u); + const uint not_finished = _not_finished.sub_then_fetch(1u); // The last worker signals to the coordinator that all work is completed. if (not_finished == 0) { diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index d4e92797039..a1f7282abe4 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "gc/shared/gcId.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/nonJavaThread.hpp" #include "runtime/semaphore.hpp" #include "utilities/debug.hpp" @@ -58,8 +59,8 @@ class WorkerTaskDispatcher { // The task currently being dispatched to the WorkerThreads. WorkerTask* _task; - volatile uint _started; - volatile uint _not_finished; + Atomic _started; + Atomic _not_finished; // Semaphore used to start the WorkerThreads. Semaphore _start_semaphore; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 41535f302d7..7a8bd55c795 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -183,8 +183,8 @@ void ShenandoahAdaptiveHeuristics::record_success_concurrent() { } } -void ShenandoahAdaptiveHeuristics::record_success_degenerated() { - ShenandoahHeuristics::record_success_degenerated(); +void ShenandoahAdaptiveHeuristics::record_degenerated() { + ShenandoahHeuristics::record_degenerated(); // Adjust both trigger's parameters in the case of a degenerated GC because // either of them should have triggered earlier to avoid this case. adjust_margin_of_error(DEGENERATE_PENALTY_SD); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 66bfc3375a3..1ba18f37c2b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -114,7 +114,7 @@ public: virtual void record_cycle_start() override; virtual void record_success_concurrent() override; - virtual void record_success_degenerated() override; + virtual void record_degenerated() override; virtual void record_success_full() override; virtual 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 478c5696188..eb740cfac61 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -243,7 +243,7 @@ void ShenandoahHeuristics::record_success_concurrent() { adjust_penalty(Concurrent_Adjust); } -void ShenandoahHeuristics::record_success_degenerated() { +void ShenandoahHeuristics::record_degenerated() { 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 3cd2cb1d171..fb8cfb36353 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -218,7 +218,7 @@ public: virtual void record_success_concurrent(); - virtual void record_success_degenerated(); + virtual void record_degenerated(); 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 8bf068df0a8..029a4dd98fb 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -766,10 +766,10 @@ void ShenandoahOldHeuristics::record_success_concurrent() { this->ShenandoahHeuristics::record_success_concurrent(); } -void ShenandoahOldHeuristics::record_success_degenerated() { +void ShenandoahOldHeuristics::record_degenerated() { // 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_success_degenerated(); + this->ShenandoahHeuristics::record_degenerated(); } 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 288d3d68e56..f38194c1ee7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -201,7 +201,7 @@ public: void record_success_concurrent() override; - void record_success_degenerated() override; + void record_degenerated() override; void record_success_full() override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index cd079d29afe..333bdbc6e72 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -313,8 +313,12 @@ void ShenandoahDegenGC::op_degenerated() { policy->record_degenerated(_generation->is_young(), _abbreviated, progress); if (progress) { heap->notify_gc_progress(); + _generation->heuristics()->record_degenerated(); } else if (!heap->mode()->is_generational() || policy->generational_should_upgrade_degenerated_gc()) { + // Upgrade to full GC, register full-GC impact on heuristics. op_degenerated_futile(); + } else { + _generation->heuristics()->record_degenerated(); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index c03e66e28da..a8c97801824 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -338,7 +338,7 @@ void ShenandoahRegionPartitions::make_all_regions_unavailable() { _empty_region_counts[partition_id] = 0; _used[partition_id] = 0; _humongous_waste[partition_id] = 0; - _available[partition_id] = FreeSetUnderConstruction; + _available[partition_id] = 0; } } @@ -2495,6 +2495,10 @@ void ShenandoahFreeSet::move_regions_from_collector_to_mutator(size_t max_xfer_r void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_trashed_regions, size_t &old_trashed_regions, size_t &first_old_region, size_t &last_old_region, size_t &old_region_count) { shenandoah_assert_heaplocked(); + assert(rebuild_lock() != nullptr, "sanity"); + rebuild_lock()->lock(false); + // This resets all state information, removing all regions from all sets. + clear(); log_debug(gc, free)("Rebuilding FreeSet"); // This places regions that have alloc_capacity into the old_collector set if they identify as is_old() or the @@ -2524,6 +2528,9 @@ void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_ _total_young_regions = _heap->num_regions() - old_region_count; _total_global_regions = _heap->num_regions(); establish_old_collector_alloc_bias(); + + // Release the rebuild lock now. What remains in this function is read-only + rebuild_lock()->unlock(); _partitions.assert_bounds(true); log_status(); } @@ -3058,7 +3065,7 @@ void ShenandoahFreeSet::log_status() { size_t max_humongous = max_contig * ShenandoahHeapRegion::region_size_bytes(); // capacity() is capacity of mutator // used() is used of mutator - size_t free = capacity() - used(); + size_t free = capacity_holding_lock() - used_holding_lock(); // Since certain regions that belonged to the Mutator free partition at the time of most recent rebuild may have been // retired, the sum of used and capacities within regions that are still in the Mutator free partition may not match // my internally tracked values of used() and free(). diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 95f9fbe6856..364637740f2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -28,9 +28,13 @@ #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" +#include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" #include "logging/logStream.hpp" +typedef ShenandoahLock ShenandoahRebuildLock; +typedef ShenandoahLocker ShenandoahRebuildLocker; + // Each ShenandoahHeapRegion is associated with a ShenandoahFreeSetPartitionId. enum class ShenandoahFreeSetPartitionId : uint8_t { Mutator, // Region is in the Mutator free set: available memory is available to mutators. @@ -139,8 +143,6 @@ public: ShenandoahRegionPartitions(size_t max_regions, ShenandoahFreeSet* free_set); ~ShenandoahRegionPartitions() {} - static const size_t FreeSetUnderConstruction = SIZE_MAX; - inline idx_t max() const { return _max; } // At initialization, reset OldCollector tallies @@ -352,6 +354,16 @@ public: return _available[int(which_partition)]; } + // Return available_in assuming caller does not hold the heap lock but does hold the rebuild_lock. + // The returned value may be "slightly stale" because we do not assure that every fetch of this value + // sees the most recent update of this value. Requiring the caller to hold the rebuild_lock assures + // that we don't see "bogus" values that are "worse than stale". During rebuild of the freeset, the + // value of _available is not reliable. + inline size_t available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId which_partition) const { + assert (which_partition < NumPartitions, "selected free set must be valid"); + return _available[int(which_partition)]; + } + // Returns bytes of humongous waste inline size_t humongous_waste(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free set must be valid"); @@ -359,23 +371,6 @@ public: return _humongous_waste[int(which_partition)]; } - // Return available_in assuming caller does not hold the heap lock. In production builds, available is - // returned without acquiring the lock. In debug builds, the global heap lock is acquired in order to - // enforce a consistency assert. - inline size_t available_in_not_locked(ShenandoahFreeSetPartitionId which_partition) const { - assert (which_partition < NumPartitions, "selected free set must be valid"); - shenandoah_assert_not_heaplocked(); -#ifdef ASSERT - ShenandoahHeapLocker locker(ShenandoahHeap::heap()->lock()); - assert((_available[int(which_partition)] == FreeSetUnderConstruction) || - (_available[int(which_partition)] == _capacity[int(which_partition)] - _used[int(which_partition)]), - "Expect available (%zu) equals capacity (%zu) - used (%zu) for partition %s", - _available[int(which_partition)], _capacity[int(which_partition)], _used[int(which_partition)], - partition_membership_name(idx_t(which_partition))); -#endif - return _available[int(which_partition)]; - } - inline void set_capacity_of(ShenandoahFreeSetPartitionId which_partition, size_t value); inline void set_used_by(ShenandoahFreeSetPartitionId which_partition, size_t value) { @@ -440,6 +435,15 @@ private: ShenandoahHeap* const _heap; ShenandoahRegionPartitions _partitions; + // This locks the rebuild process (in combination with the global heap lock). Whenever we rebuild the free set, + // we first acquire the global heap lock and then we acquire this _rebuild_lock in a nested context. Threads that + // need to check available, acquire only the _rebuild_lock to make sure that they are not obtaining the value of + // available for a partially reconstructed free-set. + // + // Note that there is rank ordering of nested locks to prevent deadlock. All threads that need to acquire both + // locks will acquire them in the same order: first the global heap lock and then the rebuild lock. + ShenandoahRebuildLock _rebuild_lock; + size_t _total_humongous_waste; HeapWord* allocate_aligned_plab(size_t size, ShenandoahAllocRequest& req, ShenandoahHeapRegion* r); @@ -635,10 +639,12 @@ private: void log_status(); public: - static const size_t FreeSetUnderConstruction = ShenandoahRegionPartitions::FreeSetUnderConstruction; - ShenandoahFreeSet(ShenandoahHeap* heap, size_t max_regions); + ShenandoahRebuildLock* rebuild_lock() { + return &_rebuild_lock; + } + inline size_t max_regions() const { return _partitions.max(); } ShenandoahFreeSetPartitionId membership(size_t index) const { return _partitions.membership(index); } inline void shrink_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId partition, @@ -776,9 +782,29 @@ public: // adjusts available with respect to lock holders. However, sequential calls to these three functions may produce // inconsistent data: available may not equal capacity - used because the intermediate states of any "atomic" // locked action can be seen by these unlocked functions. - inline size_t capacity() const { return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t used() const { return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t available() const { return _partitions.available_in_not_locked(ShenandoahFreeSetPartitionId::Mutator); } + inline size_t capacity_holding_lock() const { + shenandoah_assert_heaplocked(); + return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t capacity_not_holding_lock() { + shenandoah_assert_not_heaplocked(); + ShenandoahRebuildLocker locker(rebuild_lock()); + return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t used_holding_lock() const { + shenandoah_assert_heaplocked(); + return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t used_not_holding_lock() { + shenandoah_assert_not_heaplocked(); + ShenandoahRebuildLocker locker(rebuild_lock()); + return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t available() { + shenandoah_assert_not_heaplocked(); + ShenandoahRebuildLocker locker(rebuild_lock()); + return _partitions.available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId::Mutator); + } inline size_t total_humongous_waste() const { return _total_humongous_waste; } inline size_t humongous_waste_in_mutator() const { return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::Mutator); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 027d7e02268..fa3a7a42209 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -1113,18 +1113,17 @@ void ShenandoahFullGC::phase5_epilog() { ShenandoahPostCompactClosure post_compact; heap->heap_region_iterate(&post_compact); heap->collection_set()->clear(); - size_t young_cset_regions, old_cset_regions; - size_t first_old, last_old, num_old; - heap->free_set()->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); - - // We also do not expand old generation size following Full GC because we have scrambled age populations and - // no longer have objects separated by age into distinct regions. - if (heap->mode()->is_generational()) { - ShenandoahGenerationalFullGC::compute_balances(); + size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; + ShenandoahFreeSet* free_set = heap->free_set(); + { + free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + // We also do not expand old generation size following Full GC because we have scrambled age populations and + // no longer have objects separated by age into distinct regions. + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::compute_balances(); + } + free_set->finish_rebuild(young_cset_regions, old_cset_regions, num_old); } - - heap->free_set()->finish_rebuild(young_cset_regions, old_cset_regions, num_old); - // Set mark incomplete because the marking bitmaps have been reset except pinned regions. _generation->set_mark_incomplete(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index d74ee872cd1..a5d8cca458d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -815,10 +815,9 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); ShenandoahHeapLocker locker(heap->lock()); - size_t young_cset_regions, old_cset_regions; // We are preparing for evacuation. At this time, we ignore cset region tallies. - size_t first_old, last_old, num_old; + size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); if (heap->mode()->is_generational()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index ece4150f577..018b4898a19 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -61,7 +61,12 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : void ShenandoahGenerationalControlThread::run_service() { + // This is the only instance of request. It is important that request.generation + // does not change between a concurrent cycle failure and the start of a degenerated + // cycle. We initialize it with the young generation to handle the pathological case + // where the very first cycle is degenerated (some tests exercise this path). ShenandoahGCRequest request; + request.generation = _heap->young_generation(); while (!should_terminate()) { // Figure out if we have pending requests. @@ -77,12 +82,10 @@ void ShenandoahGenerationalControlThread::run_service() { // If the cycle was cancelled, continue the next iteration to deal with it. Otherwise, // if there was no other cycle requested, cleanup and wait for the next request. - if (!_heap->cancelled_gc()) { - MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); - if (_requested_gc_cause == GCCause::_no_gc) { - set_gc_mode(ml, none); - ml.wait(); - } + MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); + if (_requested_gc_cause == GCCause::_no_gc) { + set_gc_mode(ml, none); + ml.wait(); } } @@ -96,8 +99,7 @@ void ShenandoahGenerationalControlThread::stop_service() { log_debug(gc, thread)("Stopping control thread"); MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); _heap->cancel_gc(GCCause::_shenandoah_stop_vm); - _requested_gc_cause = GCCause::_shenandoah_stop_vm; - notify_cancellation(ml, GCCause::_shenandoah_stop_vm); + notify_control_thread(ml, GCCause::_shenandoah_stop_vm); // We can't wait here because it may interfere with the active cycle's ability // to reach a safepoint (this runs on a java thread). } @@ -105,29 +107,39 @@ void ShenandoahGenerationalControlThread::stop_service() { void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& request) { // Hold the lock while we read request cause and generation MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); - if (_heap->cancelled_gc()) { - // The previous request was cancelled. Either it was cancelled for an allocation - // failure (degenerated cycle), or old marking was cancelled to run a young collection. - // In either case, the correct generation for the next cycle can be determined by - // the cancellation cause. - request.cause = _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); - if (request.cause == GCCause::_shenandoah_concurrent_gc) { + + log_debug(gc, thread)("cancelled cause: %s, requested cause: %s", + GCCause::to_string(_heap->cancelled_cause()), GCCause::to_string(_requested_gc_cause)); + + request.cause = _requested_gc_cause; + if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) { + if (_degen_point == ShenandoahGC::_degenerated_unset) { request.generation = _heap->young_generation(); + _degen_point = ShenandoahGC::_degenerated_outside_cycle; + } else { + assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle"); } } else { - request.cause = _requested_gc_cause; + if (request.cause == GCCause::_shenandoah_concurrent_gc) { + // This is a regulator request. It is also possible that the regulator "canceled" an old mark, + // so we can clear that here. This clear operation will only clear the cancellation if it is + // a regulator request. + _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); + } request.generation = _requested_generation; - - // Only clear these if we made a request from them. In the case of a cancelled gc, - // we do not want to inadvertently lose this pending request. - _requested_gc_cause = GCCause::_no_gc; - _requested_generation = nullptr; } + log_debug(gc, thread)("request.cause: %s, request.generation: %s", + GCCause::to_string(request.cause), request.generation == nullptr ? "None" : request.generation->name()); + + _requested_gc_cause = GCCause::_no_gc; + _requested_generation = nullptr; + if (request.cause == GCCause::_no_gc || request.cause == GCCause::_shenandoah_stop_vm) { return; } + assert(request.generation != nullptr, "request.generation cannot be null, cause is: %s", GCCause::to_string(request.cause)); GCMode mode; if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) { mode = prepare_for_allocation_failure_gc(request); @@ -140,11 +152,9 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& } ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread::prepare_for_allocation_failure_gc(ShenandoahGCRequest &request) { - - if (_degen_point == ShenandoahGC::_degenerated_unset) { - _degen_point = ShenandoahGC::_degenerated_outside_cycle; - request.generation = _heap->young_generation(); - } else if (request.generation->is_old()) { + // Important: not all paths update the request.generation. This is intentional. + // A degenerated cycle must use the same generation carried over from the previous request. + if (request.generation->is_old()) { // This means we degenerated during the young bootstrap for the old generation // cycle. The following degenerated cycle should therefore also be young. request.generation = _heap->young_generation(); @@ -588,6 +598,8 @@ bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(Shenandoah if (ShenandoahCollectorPolicy::is_allocation_failure(_heap->cancelled_cause())) { assert(_degen_point == ShenandoahGC::_degenerated_unset, "Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point)); + MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); + _requested_gc_cause = _heap->cancelled_cause(); _degen_point = point; log_debug(gc, thread)("Cancellation detected:, reason: %s, degen point: %s", GCCause::to_string(_heap->cancelled_cause()), @@ -633,9 +645,7 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh void ShenandoahGenerationalControlThread::request_gc(GCCause::Cause cause) { if (ShenandoahCollectorPolicy::is_allocation_failure(cause)) { - // GC should already be cancelled. Here we are just notifying the control thread to - // wake up and handle the cancellation request, so we don't need to set _requested_gc_cause. - notify_cancellation(cause); + notify_control_thread(cause); } else if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) { handle_requested_gc(cause); } @@ -653,7 +663,8 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); if (gc_mode() == servicing_old) { if (!preempt_old_marking(generation)) { - log_debug(gc, thread)("Cannot start young, old collection is not preemptible"); + // Global should be able to cause old collection to be abandoned + log_debug(gc, thread)("Cannot start %s, old collection is not preemptible", generation->name()); return false; } @@ -661,7 +672,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera log_info(gc)("Preempting old generation mark to allow %s GC", generation->name()); while (gc_mode() == servicing_old) { ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc); - notify_cancellation(ml, GCCause::_shenandoah_concurrent_gc); + notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation); ml.wait(); } return true; @@ -695,21 +706,34 @@ void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause c void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation) { assert(_control_lock.is_locked(), "Request lock must be held here"); - log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name()); - _requested_gc_cause = cause; - _requested_generation = generation; - ml.notify(); + if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) { + // We have already observed a request to handle an allocation failure. We cannot allow + // another request (System.gc or regulator) to subvert the degenerated cycle. + log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause)); + } else { + log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name()); + _requested_gc_cause = cause; + _requested_generation = generation; + ml.notify(); + } } -void ShenandoahGenerationalControlThread::notify_cancellation(GCCause::Cause cause) { +void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause cause) { MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); - notify_cancellation(ml, cause); + notify_control_thread(ml, cause); } -void ShenandoahGenerationalControlThread::notify_cancellation(MonitorLocker& ml, GCCause::Cause cause) { - assert(_heap->cancelled_gc(), "GC should already be cancelled"); - log_debug(gc,thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause)); - ml.notify(); +void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause) { + assert(_control_lock.is_locked(), "Request lock must be held here"); + if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) { + // We have already observed a request to handle an allocation failure. We cannot allow + // another request (System.gc or regulator) to subvert the degenerated cycle. + log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause)); + } else { + log_debug(gc, thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause)); + _requested_gc_cause = cause; + ml.notify(); + } } bool ShenandoahGenerationalControlThread::preempt_old_marking(ShenandoahGeneration* generation) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp index b7dbedd5e84..13e69d25268 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp @@ -135,16 +135,13 @@ private: // Return printable name for the given gc mode. static const char* gc_mode_name(GCMode mode); - // Takes the request lock and updates the requested cause and generation, then notifies the control thread. - // The overloaded variant should be used when the _control_lock is already held. + // These notify the control thread after updating _requested_gc_cause and (optionally) _requested_generation. + // Updating the requested generation is not necessary for allocation failures nor when stopping the thread. + void notify_control_thread(GCCause::Cause cause); + void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause); void notify_control_thread(GCCause::Cause cause, ShenandoahGeneration* generation); void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation); - // Notifies the control thread, but does not update the requested cause or generation. - // The overloaded variant should be used when the _control_lock is already held. - void notify_cancellation(GCCause::Cause cause); - void notify_cancellation(MonitorLocker& ml, GCCause::Cause cause); - // Configure the heap to age objects and regions if the aging period has elapsed. void maybe_set_aging_cycle(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 736026916f7..a2ae4a68cd0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -44,13 +44,13 @@ public: void post_initialize_heuristics() override; static ShenandoahGenerationalHeap* heap() { - assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap"); + assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap"); CollectedHeap* heap = Universe::heap(); return cast(heap); } static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) { - assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap"); + assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap"); return checked_cast(heap); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 3bf53f800a2..683e2959a92 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -426,8 +426,6 @@ jint ShenandoahHeap::initialize() { _affiliations[i] = ShenandoahAffiliation::FREE; } _free_set = new ShenandoahFreeSet(this, _num_regions); - - post_initialize_heuristics(); // We are initializing free set. We ignore cset region tallies. size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; @@ -1658,7 +1656,7 @@ void ShenandoahHeap::verify(VerifyOption vo) { } } size_t ShenandoahHeap::tlab_capacity() const { - return _free_set->capacity(); + return _free_set->capacity_not_holding_lock(); } class ObjectIterateScanRootClosure : public BasicOopIterateClosure { @@ -2138,7 +2136,7 @@ GCTracer* ShenandoahHeap::tracer() { } size_t ShenandoahHeap::tlab_used() const { - return _free_set->used(); + return _free_set->used_not_holding_lock(); } bool ShenandoahHeap::try_cancel_gc(GCCause::Cause cause) { @@ -2528,8 +2526,7 @@ void ShenandoahHeap::rebuild_free_set(bool concurrent) { ShenandoahPhaseTimings::final_update_refs_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_update_refs_rebuild_freeset); ShenandoahHeapLocker locker(lock()); - size_t young_cset_regions, old_cset_regions; - size_t first_old_region, last_old_region, old_region_count; + size_t young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count; _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count); // If there are no old regions, first_old_region will be greater than last_old_region assert((first_old_region > last_old_region) || @@ -2548,13 +2545,14 @@ void ShenandoahHeap::rebuild_free_set(bool concurrent) { // The computation of bytes_of_allocation_runway_before_gc_trigger is quite conservative so consider all of this // available for transfer to old. Note that transfer of humongous regions does not impact available. ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); - size_t allocation_runway = gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions); + size_t allocation_runway = + gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions); gen_heap->compute_old_generation_balance(allocation_runway, old_cset_regions); // Total old_available may have been expanded to hold anticipated promotions. We trigger if the fragmented available // memory represents more than 16 regions worth of data. Note that fragmentation may increase when we promote regular - // regions in place when many of these regular regions have an abundant amount of available memory within them. Fragmentation - // will decrease as promote-by-copy consumes the available memory within these partially consumed regions. + // regions in place when many of these regular regions have an abundant amount of available memory within them. + // Fragmentation will decrease as promote-by-copy consumes the available memory within these partially consumed regions. // // We consider old-gen to have excessive fragmentation if more than 12.5% of old-gen is free memory that resides // within partially consumed regions of memory. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp index d774a8dba42..81c62ebbda9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp @@ -30,7 +30,7 @@ ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set) : _free_set(free_set) - , _used_before(free_set->used()) + , _used_before(free_set->used_not_holding_lock()) , _if_before(free_set->internal_fragmentation()) , _ef_before(free_set->external_fragmentation()) { } @@ -38,7 +38,6 @@ ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set bool ShenandoahMetricsSnapshot::is_good_progress() const { // Under the critical threshold? const size_t free_actual = _free_set->available(); - assert(free_actual != ShenandoahFreeSet::FreeSetUnderConstruction, "Avoid this race"); // ShenandoahCriticalFreeThreshold is expressed as a percentage. We multiply this percentage by 1/100th // of the soft max capacity to determine whether the available memory within the mutator partition of the @@ -52,7 +51,7 @@ bool ShenandoahMetricsSnapshot::is_good_progress() const { } // Freed up enough? - const size_t used_after = _free_set->used(); + const size_t used_after = _free_set->used_not_holding_lock(); const size_t progress_actual = (_used_before > used_after) ? _used_before - used_after : 0; const size_t progress_expected = ShenandoahHeapRegion::region_size_bytes(); const bool prog_used = progress_actual >= progress_expected; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index c7cf013d034..c795eda3d96 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -412,9 +412,12 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); + ShenandoahFreeSet* free_set = heap->free_set(); ShenandoahHeapLocker locker(heap->lock()); - size_t young_trash_regions, old_trash_regions; - size_t first_old, last_old, num_old; + + // This is completion of old-gen marking. We rebuild in order to reclaim immediate garbage and to + // prepare for subsequent mixed evacuations. + size_t young_trash_regions, old_trash_regions, first_old, last_old, num_old; heap->free_set()->prepare_to_rebuild(young_trash_regions, old_trash_regions, first_old, last_old, num_old); // At the end of old-gen, we may find that we have reclaimed immediate garbage, allowing a longer allocation runway. // We may also find that we have accumulated canddiate regions for mixed evacuation. If so, we will want to expand diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index 964b6f0a10a..ec4b7c7217c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -149,6 +149,13 @@ bool ShenandoahRegulatorThread::start_global_cycle() const { bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const { double now = os::elapsedTime(); + + // This call may find the control thread waiting on workers which have suspended + // to allow a safepoint to run. If this regulator thread does not yield, the safepoint + // will not run. The worker threads won't progress, the control thread won't progress, + // and the regulator thread may never yield. Therefore, we leave the suspendible + // thread set before making this call. + SuspendibleThreadSetLeaver leaver; bool accepted = _control_thread->request_concurrent_gc(generation); if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) { double wait_time = os::elapsedTime() - now; diff --git a/src/hotspot/share/jfr/jfr.cpp b/src/hotspot/share/jfr/jfr.cpp index b09a89594ad..d9892f80b6f 100644 --- a/src/hotspot/share/jfr/jfr.cpp +++ b/src/hotspot/share/jfr/jfr.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 @@ -32,6 +32,7 @@ #include "jfr/recorder/repository/jfrEmergencyDump.hpp" #include "jfr/recorder/repository/jfrRepository.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/service/jfrRecorderService.hpp" #include "jfr/support/jfrClassDefineEvent.hpp" #include "jfr/support/jfrKlassExtension.hpp" #include "jfr/support/jfrResolution.hpp" @@ -43,6 +44,7 @@ #include "runtime/java.hpp" #include "runtime/javaThread.hpp" + bool Jfr::is_enabled() { return JfrRecorder::is_enabled(); } @@ -153,9 +155,9 @@ void Jfr::on_resolution(const Method* caller, const Method* target, TRAPS) { } #endif -void Jfr::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt) { +void Jfr::on_vm_shutdown(bool exception_handler /* false */, bool halt /* false */, bool oom /* false */) { if (!halt && JfrRecorder::is_recording()) { - JfrEmergencyDump::on_vm_shutdown(emit_old_object_samples, emit_event_shutdown); + JfrEmergencyDump::on_vm_shutdown(exception_handler, oom); } } @@ -173,6 +175,12 @@ bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* de return JfrOptionSet::parse_start_flight_recording_option(option, delimiter); } +void Jfr::on_report_java_out_of_memory() { + if (CrashOnOutOfMemoryError && JfrRecorder::is_recording()) { + JfrRecorderService::emit_leakprofiler_events_on_oom(); + } +} + #if INCLUDE_CDS void Jfr::on_restoration(const Klass* k, JavaThread* jt) { assert(k != nullptr, "invariant"); diff --git a/src/hotspot/share/jfr/jfr.hpp b/src/hotspot/share/jfr/jfr.hpp index db567cc9a29..ac6a232dda1 100644 --- a/src/hotspot/share/jfr/jfr.hpp +++ b/src/hotspot/share/jfr/jfr.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 @@ -71,7 +71,7 @@ class Jfr : AllStatic { static void on_resolution(const Method* caller, const Method* target, TRAPS); static void on_java_thread_start(JavaThread* starter, JavaThread* startee); static void on_set_current_thread(JavaThread* jt, oop thread); - static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt = false); + static void on_vm_shutdown(bool exception_handler = false, bool halt = false, bool oom = false); static void on_vm_error_report(outputStream* st); static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter); static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter); @@ -79,6 +79,7 @@ class Jfr : AllStatic { static void initialize_main_thread(JavaThread* jt); static bool has_sample_request(JavaThread* jt); static void check_and_process_sample_request(JavaThread* jt); + static void on_report_java_out_of_memory(); CDS_ONLY(static void on_restoration(const Klass* k, JavaThread* jt);) }; diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index 6a1146587bc..d8a30f9b5ee 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.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 @@ -364,8 +364,7 @@ JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jclass jvm, jb JVM_END NO_TRANSITION(void, jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean emit_all, jboolean skip_bfs)) - JfrRecorderService service; - service.emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE); + JfrRecorderService::emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE); NO_TRANSITION_END JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jclass jvm, jobject t)) diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp index 7e7747ba396..0a8b3975139 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.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 @@ -232,41 +232,50 @@ void JfrSamplerThread::task_stacktrace(JfrSampleRequestType type, JavaThread** l JavaThread* start = nullptr; elapsedTimer sample_time; sample_time.start(); - ThreadsListHandle tlh; - // Resolve a sample session relative start position index into the thread list array. - // In cases where the last sampled thread is null or not-null but stale, find_index() returns -1. - _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread); - JavaThread* current = _cur_index != -1 ? *last_thread : nullptr; + { + /* + * Take the Threads_lock for three purposes: + * + * 1) Avoid sampling right through a safepoint, + * which could result in touching oops in case of virtual threads. + * 2) Prevent JFR from issuing an epoch rotation while the sampler thread + * is actively processing a thread in state native, as both threads are outside the safepoint protocol. + * 3) Some operating systems (BSD / Mac) require a process lock when sending a signal with pthread_kill. + * Holding the Threads_lock prevents a JavaThread from calling os::create_thread(), which also takes the process lock. + * In a sense, we provide a coarse signal mask, so we can always send the resume signal. + */ + MutexLocker tlock(Threads_lock); + ThreadsListHandle tlh; + // Resolve a sample session relative start position index into the thread list array. + // In cases where the last sampled thread is null or not-null but stale, find_index() returns -1. + _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread); + JavaThread* current = _cur_index != -1 ? *last_thread : nullptr; - while (num_samples < sample_limit) { - current = next_thread(tlh.list(), start, current); - if (current == nullptr) { - break; - } - if (is_excluded(current)) { - continue; - } - if (start == nullptr) { - start = current; // remember the thread where we started to attempt sampling - } - bool success; - if (JAVA_SAMPLE == type) { - success = sample_java_thread(current); - } else { - assert(type == NATIVE_SAMPLE, "invariant"); - success = sample_native_thread(current); - } - if (success) { - num_samples++; - } - if (SafepointSynchronize::is_at_safepoint()) { - // For _thread_in_native, we cannot get the Threads_lock. - // For _thread_in_Java, well, there are none. - break; + while (num_samples < sample_limit) { + current = next_thread(tlh.list(), start, current); + if (current == nullptr) { + break; + } + if (is_excluded(current)) { + continue; + } + if (start == nullptr) { + start = current; // remember the thread where we started to attempt sampling + } + bool success; + if (JAVA_SAMPLE == type) { + success = sample_java_thread(current); + } else { + assert(type == NATIVE_SAMPLE, "invariant"); + success = sample_native_thread(current); + } + if (success) { + num_samples++; + } } + + *last_thread = current; // remember the thread we last attempted to sample } - - *last_thread = current; // remember the thread we last attempted to sample sample_time.stop(); log_trace(jfr)("JFR thread sampling done in %3.7f secs with %d java %d native samples", sample_time.seconds(), type == JAVA_SAMPLE ? num_samples : 0, type == NATIVE_SAMPLE ? num_samples : 0); @@ -297,6 +306,7 @@ class OSThreadSampler : public SuspendedThreadTask { // Sampling a thread in state _thread_in_Java // involves a platform-specific thread suspend and CPU context retrieval. bool JfrSamplerThread::sample_java_thread(JavaThread* jt) { + assert_lock_strong(Threads_lock); if (jt->thread_state() != _thread_in_Java) { return false; } @@ -328,6 +338,7 @@ static JfrSamplerThread* _sampler_thread = nullptr; // without thread suspension and CPU context retrieval, // if we carefully order the loads of the thread state. bool JfrSamplerThread::sample_native_thread(JavaThread* jt) { + assert_lock_strong(Threads_lock); if (jt->thread_state() != _thread_in_native) { return false; } @@ -343,24 +354,14 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) { SafepointMechanism::arm_local_poll_release(jt); - // Take the Threads_lock for two purposes: - // 1) Avoid sampling through a safepoint which could result - // in touching oops in case of virtual threads. - // 2) Prevent JFR from issuing an epoch rotation while the sampler thread - // is actively processing a thread in native, as both threads are now - // outside the safepoint protocol. - - // OrderAccess::fence() as part of acquiring the lock prevents loads from floating up. - JfrMutexTryLock threads_lock(Threads_lock); - - if (!threads_lock.acquired() || !jt->has_last_Java_frame()) { - // Remove the native sample request and release the potentially waiting thread. - JfrSampleMonitor jsm(tl); - return false; + // Separate the arming of the poll (above) from the reading of JavaThread state (below). + if (UseSystemMemoryBarrier) { + SystemMemoryBarrier::emit(); + } else { + OrderAccess::fence(); } - if (jt->thread_state() != _thread_in_native) { - assert_lock_strong(Threads_lock); + if (jt->thread_state() != _thread_in_native || !jt->has_last_Java_frame()) { JfrSampleMonitor jsm(tl); if (jsm.is_waiting()) { // The thread has already returned from native, diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index 309ae961808..cdebcbcfcb5 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.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 @@ -38,6 +38,8 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" @@ -460,15 +462,6 @@ static void release_locks(Thread* thread) { assert(thread != nullptr, "invariant"); assert(!thread->is_Java_thread() || JavaThread::cast(thread)->thread_state() == _thread_in_vm, "invariant"); -#ifdef ASSERT - Mutex* owned_lock = thread->owned_locks(); - while (owned_lock != nullptr) { - Mutex* next = owned_lock->next(); - owned_lock->unlock(); - owned_lock = next; - } -#endif // ASSERT - if (Threads_lock->owned_by_self()) { Threads_lock->unlock(); } @@ -550,17 +543,14 @@ class JavaThreadInVMAndNative : public StackObj { } }; -static void post_events(bool emit_old_object_samples, bool emit_event_shutdown, Thread* thread) { - if (emit_old_object_samples) { - LeakProfiler::emit_events(max_jlong, false, false); - } - if (emit_event_shutdown) { +static void post_events(bool exception_handler, bool oom, Thread * thread) { + if (exception_handler) { EventShutdown e; - e.set_reason("VM Error"); + e.set_reason(oom ? "CrashOnOutOfMemoryError" : "VM Error"); e.commit(); } EventDumpReason event; - event.set_reason(emit_old_object_samples ? "Out of Memory" : "Crash"); + event.set_reason(exception_handler && oom ? "CrashOnOutOfMemoryError" : exception_handler ? "Crash" : "Out of Memory"); event.set_recordingId(-1); event.commit(); } @@ -594,20 +584,40 @@ static bool guard_reentrancy() { return false; } -void JfrEmergencyDump::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown) { +void JfrEmergencyDump::on_vm_shutdown(bool exception_handler, bool oom) { if (!guard_reentrancy()) { return; } + Thread* const thread = Thread::current_or_null_safe(); assert(thread != nullptr, "invariant"); - if (thread->is_Watcher_thread()) { - log_info(jfr, system)("The Watcher thread crashed so no jfr emergency dump will be generated."); - return; - } + // Ensure a JavaThread is _thread_in_vm when we make this call JavaThreadInVMAndNative jtivm(thread); + post_events(exception_handler, oom, thread); + + if (thread->is_Watcher_thread()) { + // We cannot attempt an emergency dump using the Watcher thread + // because we rely on the WatcherThread task "is_error_reported()", + // to exit the VM after a hardcoded timeout, should the relatively + // risky operation of an emergency dump fail (deadlock, livelock). + log_warning(jfr, system) + ("The Watcher thread crashed so no jfr emergency dump will be generated."); + return; + } + + if (thread->is_VM_thread()) { + const VM_Operation* const operation = VMThread::vm_operation(); + if (operation != nullptr && operation->type() == VM_Operation::VMOp_JFROldObject) { + // We will not be able to issue a rotation because the rotation lock + // is held by the JFR Recorder Thread that issued the VM_Operation. + log_warning(jfr, system) + ("The VM Thread crashed as part of emitting leak profiler events so no jfr emergency dump will be generated."); + return; + } + } + release_locks(thread); - post_events(emit_old_object_samples, emit_event_shutdown, thread); // if JavaThread, transition to _thread_in_native to issue a final flushpoint NoHandleMark nhm; diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp index 04c2851a516..b337d73364a 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.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,7 @@ class JfrEmergencyDump : AllStatic { static const char* chunk_path(const char* repository_path); static void on_vm_error(const char* repository_path); static void on_vm_error_report(outputStream* st, const char* repository_path); - static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown); + static void on_vm_shutdown(bool exception_handler, bool oom); }; #endif // SHARE_JFR_RECORDER_REPOSITORY_JFREMERGENCYDUMP_HPP diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp index a9ba456ad76..6db7a42ab27 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp @@ -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 @@ -34,7 +34,8 @@ (MSGBIT(MSG_START)) | \ (MSGBIT(MSG_CLONE_IN_MEMORY)) | \ (MSGBIT(MSG_VM_ERROR)) | \ - (MSGBIT(MSG_FLUSHPOINT)) \ + (MSGBIT(MSG_FLUSHPOINT)) | \ + (MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) \ ) static JfrPostBox* _instance = nullptr; @@ -165,7 +166,7 @@ void JfrPostBox::notify_waiters() { assert(JfrMsg_lock->owned_by_self(), "incrementing _msg_handled_serial is protected by JfrMsg_lock."); // Update made visible on release of JfrMsg_lock via fence instruction in Monitor::IUnlock. ++_msg_handled_serial; - JfrMsg_lock->notify(); + JfrMsg_lock->notify_all(); } // safeguard to ensure no threads are left waiting diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp index 10457261643..92f70b1dc9b 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp +++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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 @@ -43,6 +43,7 @@ enum JFR_Msg { MSG_SHUTDOWN, MSG_VM_ERROR, MSG_FLUSHPOINT, + MSG_EMIT_LEAKP_REFCHAINS, MSG_NO_OF_MSGS }; @@ -51,23 +52,25 @@ enum JFR_Msg { * * Synchronous messages (posting thread waits for message completion): * - * MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1 - * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2 - * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4 - * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8 - * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100 - * MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200 + * MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1 + * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2 + * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4 + * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8 + * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100 + * MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200 + * MSG_EMIT_LEAKP_REFCHAINS (10); MSGBIT(MSG_EMIT_LEAKP_REFCHAINS) == (1 << 0xa) == 0x400 * * Asynchronous messages (posting thread returns immediately upon deposit): * - * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10 - * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20 - * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40 - * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80 + * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10 + * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20 + * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40 + * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80 */ class JfrPostBox : public JfrCHeapObj { friend class JfrRecorder; + friend class JfrRecorderService; public: void post(JFR_Msg msg); diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index 08250a1ae59..6f8d44fb1a4 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.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 @@ -55,6 +55,7 @@ #include "runtime/safepoint.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" +#include "utilities/growableArray.hpp" // incremented on each flushpoint static u8 flushpoint_id = 0; @@ -391,6 +392,7 @@ class JfrSafepointWriteVMOperation : public VM_Operation { JfrRecorderService::JfrRecorderService() : _checkpoint_manager(JfrCheckpointManager::instance()), _chunkwriter(JfrRepository::chunkwriter()), + _post_box(JfrPostBox::instance()), _repository(JfrRepository::instance()), _stack_trace_repository(JfrStackTraceRepository::instance()), _storage(JfrStorage::instance()), @@ -670,17 +672,173 @@ void JfrRecorderService::evaluate_chunk_size_for_rotation() { JfrChunkRotation::evaluate(_chunkwriter); } -void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs) { - DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current())); - // Take the rotation lock to exclude flush() during event emits. This is because event emit - // also creates a number checkpoint events. Those checkpoint events require a future typeset checkpoint - // event for completeness, i.e. to be generated before being flushed to a segment. - // The upcoming flush() or rotation() after event emit completes this typeset checkpoint - // and serializes all event emit checkpoint events to the same segment. - JfrRotationLock lock; - // Take the rotation lock before the transition. - JavaThread* current_thread = JavaThread::current(); - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread)); - ThreadInVMfromNative transition(current_thread); - LeakProfiler::emit_events(cutoff_ticks, emit_all, skip_bfs); +// LeakProfiler event serialization support. + +struct JfrLeakProfilerEmitRequest { + int64_t cutoff_ticks; + bool emit_all; + bool skip_bfs; + bool oom; +}; + +typedef GrowableArrayCHeap JfrLeakProfilerEmitRequestQueue; +static JfrLeakProfilerEmitRequestQueue* _queue = nullptr; +constexpr const static int64_t _no_path_to_gc_roots = 0; +static bool _oom_emit_request_posted = false; +static bool _oom_emit_request_delivered = false; + +static inline bool exclude_paths_to_gc_roots(int64_t cutoff_ticks) { + return cutoff_ticks <= _no_path_to_gc_roots; +} + +static void enqueue(const JfrLeakProfilerEmitRequest& request) { + assert(JfrRotationLock::is_owner(), "invariant"); + if (_queue == nullptr) { + _queue = new JfrLeakProfilerEmitRequestQueue(4); + } + assert(_queue != nullptr, "invariant"); + assert(!_oom_emit_request_posted, "invariant"); + if (request.oom) { + _oom_emit_request_posted = true; + } + _queue->append(request); +} + +static JfrLeakProfilerEmitRequest dequeue() { + assert(JfrRotationLock::is_owner(), "invariant"); + assert(_queue != nullptr, "invariant"); + assert(_queue->is_nonempty(), "invariant"); + const JfrLeakProfilerEmitRequest& request = _queue->first(); + _queue->remove_at(0); + return request; +} + +// This version of emit excludes path-to-gc-roots, i.e. it skips reference chains. +static void emit_leakprofiler_events(bool emit_all, bool skip_bfs, JavaThread* jt) { + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + // Take the rotation lock to exclude flush() during event emits. This is because the event emit operation + // also creates a number of checkpoint events. Those checkpoint events require a future typeset checkpoint + // event for completeness, i.e., to be generated before being flushed to a segment. + // The upcoming flush() or rotation() after event emit completes this typeset checkpoint + // and serializes all checkpoint events to the same segment. + JfrRotationLock lock; + // Take the rotation lock before the thread transition, to avoid blocking safepoints. + if (_oom_emit_request_posted) { + // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError + // is pending or has already been completed. We are about to crash at any time now. + assert(CrashOnOutOfMemoryError, "invariant"); + return; + } + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); + ThreadInVMfromNative transition(jt); + // Since we are not requesting path-to-gc-roots, i.e., reference chains, we need not issue a VM_Operation. + // Therefore, we can let the requesting thread process the request directly, since it already holds the requisite lock. + LeakProfiler::emit_events(_no_path_to_gc_roots, emit_all, skip_bfs); +} + +void JfrRecorderService::transition_and_post_leakprofiler_emit_msg(JavaThread* jt) { + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);) + assert(!JfrRotationLock::is_owner(), "invariant"); + // Transition to _thread_in_VM and post a synchronous message to the JFR Recorder Thread + // for it to process our enqueued request, which includes paths-to-gc-roots, i.e., reference chains. + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); + ThreadInVMfromNative transition(jt); + _post_box.post(MSG_EMIT_LEAKP_REFCHAINS); +} + +// This version of emit includes path-to-gc-roots, i.e., it includes in the request traversing of reference chains. +// Traversing reference chains is performed as part of a VM_Operation, and we initiate it from the JFR Recorder Thread. +// Because multiple threads can concurrently report_on_java_out_of_memory(), having them all post a synchronous JFR msg, +// they rendezvous at a safepoint in a convenient state, ThreadBlockInVM. This mechanism prevents any thread from racing past +// this point and begin executing VMError::report_and_die(), until at least one oom request has been delivered. +void JfrRecorderService::emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs, + bool oom, + JavaThread* jt) { + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);) + assert(!exclude_paths_to_gc_roots(cutoff_ticks), "invariant"); + + { + JfrRotationLock lock; + // Take the rotation lock to read and post a request for the JFR Recorder Thread. + if (_oom_emit_request_posted) { + if (!oom) { + // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError + // is pending or has already been completed. We are about to crash at any time now. + assert(CrashOnOutOfMemoryError, "invariant"); + return; + } + } else { + assert(!_oom_emit_request_posted, "invariant"); + JfrLeakProfilerEmitRequest request = { cutoff_ticks, emit_all, skip_bfs, oom }; + enqueue(request); + } + } + JfrRecorderService service; + service.transition_and_post_leakprofiler_emit_msg(jt); +} + +// Leakprofiler serialization request, the jdk.jfr.internal.JVM.emitOldObjectSamples() Java entry point. +void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs) { + JavaThread* const jt = JavaThread::current(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);) + if (exclude_paths_to_gc_roots(cutoff_ticks)) { + ::emit_leakprofiler_events(emit_all, skip_bfs, jt); + return; + } + emit_leakprofiler_events_paths_to_gc_roots(cutoff_ticks, emit_all, skip_bfs, /* oom */ false, jt); +} + +// Leakprofiler serialization request, the report_on_java_out_of_memory VM entry point. +void JfrRecorderService::emit_leakprofiler_events_on_oom() { + assert(CrashOnOutOfMemoryError, "invariant"); + if (EventOldObjectSample::is_enabled()) { + JavaThread* const jt = JavaThread::current(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);) + ThreadToNativeFromVM transition(jt); + emit_leakprofiler_events_paths_to_gc_roots(max_jlong, false, false, /* oom */ true, jt); + } +} + +// The worker routine for the JFR Recorder Thread when processing MSG_EMIT_LEAKP_REFCHAINS messages. +void JfrRecorderService::emit_leakprofiler_events() { + JavaThread* const jt = JavaThread::current(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + // Take the rotation lock before the transition. + JfrRotationLock lock; + if (_oom_emit_request_delivered) { + // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError + // has already been completed. We are about to crash at any time now. + assert(_oom_emit_request_posted, "invariant"); + assert(CrashOnOutOfMemoryError, "invariant"); + return; + } + + assert(_queue->is_nonempty(), "invariant"); + + { + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); + ThreadInVMfromNative transition(jt); + while (_queue->is_nonempty()) { + const JfrLeakProfilerEmitRequest& request = dequeue(); + LeakProfiler::emit_events(request.cutoff_ticks, request.emit_all, request.skip_bfs); + if (_oom_emit_request_posted && request.oom) { + assert(CrashOnOutOfMemoryError, "invariant"); + _oom_emit_request_delivered = true; + break; + } + } + } + + // If processing involved an out-of-memory request, issue an immediate flush operation. + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + if (_chunkwriter.is_valid() && _oom_emit_request_delivered) { + invoke_flush(); + } } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp index e5b4500afc0..3759ff98828 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.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 @@ -27,19 +27,23 @@ #include "jfr/utilities/jfrAllocation.hpp" +class JavaThread; class JfrCheckpointManager; class JfrChunkWriter; +class JfrPostBox; class JfrRepository; class JfrStackTraceRepository; class JfrStorage; class JfrStringPool; class JfrRecorderService : public StackObj { + friend class Jfr; friend class JfrSafepointClearVMOperation; friend class JfrSafepointWriteVMOperation; private: JfrCheckpointManager& _checkpoint_manager; JfrChunkWriter& _chunkwriter; + JfrPostBox& _post_box; JfrRepository& _repository; JfrStackTraceRepository& _stack_trace_repository; JfrStorage& _storage; @@ -64,6 +68,14 @@ class JfrRecorderService : public StackObj { void invoke_safepoint_write(); void post_safepoint_write(); + void transition_and_post_leakprofiler_emit_msg(JavaThread* jt); + + static void emit_leakprofiler_events_on_oom(); + static void emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs, + bool oom, + JavaThread* jt); public: JfrRecorderService(); void start(); @@ -72,8 +84,12 @@ class JfrRecorderService : public StackObj { void flushpoint(); void process_full_buffers(); void evaluate_chunk_size_for_rotation(); - void emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs); + void emit_leakprofiler_events(); + static bool is_recording(); + static void emit_leakprofiler_events(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs); }; #endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp index de015e9a502..bd01adf5b3a 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.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 @@ -44,6 +44,7 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { #define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP))) #define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT))) #define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER))) + #define LEAKPROFILER_REFCHAINS (msgs & MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) JfrPostBox& post_box = JfrRecorderThreadEntry::post_box(); log_debug(jfr, system)("Recorder thread STARTED"); @@ -70,6 +71,9 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { if (PROCESS_FULL_BUFFERS) { service.process_full_buffers(); } + if (LEAKPROFILER_REFCHAINS) { + service.emit_leakprofiler_events(); + } // Check amount of data written to chunk already // if it warrants asking for a new chunk. service.evaluate_chunk_size_for_rotation(); @@ -98,5 +102,5 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { #undef ROTATE #undef FLUSHPOINT #undef PROCESS_FULL_BUFFERS - #undef SCAVENGE + #undef LEAKPROFILER_REFCHAINS } diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 4cf72fbde4b..fac8596173f 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1031,6 +1031,15 @@ bool CallNode::is_call_to_arraycopystub() const { return false; } +bool CallNode::is_call_to_multianewarray_stub() const { + if (_name != nullptr && + strstr(_name, "multianewarray") != nullptr && + strstr(_name, "C2 runtime") != nullptr) { + return true; + } + return false; +} + //============================================================================= uint CallJavaNode::size_of() const { return sizeof(*this); } bool CallJavaNode::cmp( const Node &n ) const { diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index de4b8941359..0bb064efc43 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -758,6 +758,7 @@ public: virtual uint match_edge(uint idx) const; bool is_call_to_arraycopystub() const; + bool is_call_to_multianewarray_stub() const; virtual void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode* sfpt) {} diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index db4fedbba3b..ed72d8a11cf 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1112,8 +1112,6 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { if( !ti->is_con() ) return nullptr; jint con = ti->get_con(); - Node *hook = new Node(1); - // First, special check for modulo 2^k-1 if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) { uint k = exact_log2(con+1); // Extract k @@ -1129,7 +1127,9 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *x = in(1); // Value being mod'd Node *divisor = in(2); // Also is mask - hook->init_req(0, x); // Add a use to x to prevent him from dying + // Add a use to x to prevent it from dying + Node* hook = new Node(1); + hook->init_req(0, x); // Generate code to reduce X rapidly to nearly 2^k-1. for( int i = 0; i < trip_count; i++ ) { Node *xl = phase->transform( new AndINode(x,divisor) ); @@ -1185,6 +1185,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Save in(1) so that it cannot be changed or deleted + Node* hook = new Node(1); hook->init_req(0, in(1)); // Divide using the transform from DivI to MulL @@ -1407,8 +1408,6 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( !tl->is_con() ) return nullptr; jlong con = tl->get_con(); - Node *hook = new Node(1); - // Expand mod if(con >= 0 && con < max_jlong && is_power_of_2(con + 1)) { uint k = log2i_exact(con + 1); // Extract k @@ -1426,13 +1425,15 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *x = in(1); // Value being mod'd Node *divisor = in(2); // Also is mask - hook->init_req(0, x); // Add a use to x to prevent him from dying + // Add a use to x to prevent it from dying + Node* hook = new Node(1); + hook->init_req(0, x); // Generate code to reduce X rapidly to nearly 2^k-1. for( int i = 0; i < trip_count; i++ ) { Node *xl = phase->transform( new AndLNode(x,divisor) ); Node *xh = phase->transform( new RShiftLNode(x,phase->intcon(k)) ); // Must be signed x = phase->transform( new AddLNode(xh,xl) ); - hook->set_req(0, x); // Add a use to x to prevent him from dying + hook->set_req(0, x); // Add a use to x to prevent it from dying } // Generate sign-fixup code. Was original value positive? @@ -1482,6 +1483,8 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Save in(1) so that it cannot be changed or deleted + // Add a use to x to prevent him from dying + Node* hook = new Node(1); hook->init_req(0, in(1)); // Divide using the transform from DivL to MulL diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index b067d93176a..792384b1ec1 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.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 @@ -1058,6 +1058,39 @@ void ConnectionGraph::updates_after_load_split(Node* data_phi, Node* previous_lo // "new_load" might actually be a constant, parameter, etc. if (new_load->is_Load()) { Node* new_addp = new_load->in(MemNode::Address); + + // If new_load is a Load but not from an AddP, it means that the load is folded into another + // load. And since this load is not from a field, we cannot create a unique type for it. + // For example: + // + // if (b) { + // Holder h1 = new Holder(); + // Object o = ...; + // h.o = o.getClass(); + // } else { + // Holder h2 = ...; + // } + // Holder h = Phi(h1, h2); + // Object r = h.o; + // + // Then, splitting r through the merge point results in: + // + // if (b) { + // Holder h1 = new Holder(); + // Object o = ...; + // h.o = o.getClass(); + // Object o1 = h.o; + // } else { + // Holder h2 = ...; + // Object o2 = h2.o; + // } + // Object r = Phi(o1, o2); + // + // In this case, o1 is folded to o.getClass() which is a Load but not from an AddP, but from + // an OopHandle that is loaded from the Klass of o. + if (!new_addp->is_AddP()) { + continue; + } Node* base = get_addp_base(new_addp); // The base might not be something that we can create an unique @@ -2066,8 +2099,7 @@ void ConnectionGraph::add_call_node(CallNode* call) { // Use bytecode estimator to record whether the call's return value escapes. ciMethod* meth = call->as_CallJava()->method(); if (meth == nullptr) { - const char* name = call->as_CallStaticJava()->_name; - assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "TODO: add failed case check"); + assert(call->as_CallStaticJava()->is_call_to_multianewarray_stub(), "TODO: add failed case check"); // Returns a newly allocated non-escaped object. add_java_object(call, PointsToNode::NoEscape); set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of multinewarray")); @@ -2775,8 +2807,7 @@ int ConnectionGraph::find_init_values_phantom(JavaObjectNode* pta) { assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity"); #ifdef ASSERT if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == nullptr) { - const char* name = alloc->as_CallStaticJava()->_name; - assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "sanity"); + assert(alloc->as_CallStaticJava()->is_call_to_multianewarray_stub(), "sanity"); } #endif // Non-escaped allocation returned from Java or runtime call have unknown values in fields. diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 5b76f5b42cf..0d4fb6791a4 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -3913,7 +3913,6 @@ const Type* SCMemProjNode::Value(PhaseGVN* phase) const LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required ) : Node(required), _type(rt), - _adr_type(at), _barrier_data(0) { init_req(MemNode::Control, c ); @@ -3921,6 +3920,7 @@ LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const Ty init_req(MemNode::Address, adr); init_req(MemNode::ValueIn, val); init_class_id(Class_LoadStore); + DEBUG_ONLY(_adr_type = at; adr_type();) } //------------------------------Value----------------------------------------- @@ -3944,6 +3944,11 @@ const Type* LoadStoreNode::Value(PhaseGVN* phase) const { return bottom_type(); } +const TypePtr* LoadStoreNode::adr_type() const { + const TypePtr* cross_check = DEBUG_ONLY(_adr_type) NOT_DEBUG(nullptr); + return MemNode::calculate_adr_type(in(MemNode::Address)->bottom_type(), cross_check); +} + uint LoadStoreNode::ideal_reg() const { return _type->ideal_reg(); } diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index d554c037012..e84556528e6 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -797,11 +797,6 @@ public: virtual int Opcode() const; virtual bool is_CFG() const { return false; } virtual const Type *bottom_type() const {return Type::MEMORY;} - virtual const TypePtr *adr_type() const { - Node* ctrl = in(0); - if (ctrl == nullptr) return nullptr; // node is dead - return ctrl->in(MemNode::Memory)->adr_type(); - } virtual uint ideal_reg() const { return 0;} // memory projections don't have a register virtual const Type* Value(PhaseGVN* phase) const; #ifndef PRODUCT @@ -814,9 +809,11 @@ public: class LoadStoreNode : public Node { private: const Type* const _type; // What kind of value is loaded? - const TypePtr* _adr_type; // What kind of memory is being addressed? uint8_t _barrier_data; // Bit field with barrier information virtual uint size_of() const; // Size is bigger +#ifdef ASSERT + const TypePtr* _adr_type; // What kind of memory is being addressed? +#endif // ASSERT public: LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required ); virtual bool depends_only_on_test() const { return false; } @@ -824,7 +821,7 @@ public: virtual const Type *bottom_type() const { return _type; } virtual uint ideal_reg() const; - virtual const class TypePtr *adr_type() const { return _adr_type; } // returns bottom_type of address + virtual const TypePtr* adr_type() const; virtual const Type* Value(PhaseGVN* phase) const; bool result_not_used() const; diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1e4cd42e09a..c4bdc5e8903 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -762,26 +762,36 @@ bool PhaseGVN::is_dominator_helper(Node *d, Node *n, bool linear_only) { //------------------------------dead_loop_check-------------------------------- // Check for a simple dead loop when a data node references itself directly // or through an other data node excluding cons and phis. -void PhaseGVN::dead_loop_check( Node *n ) { - // Phi may reference itself in a loop - if (n != nullptr && !n->is_dead_loop_safe() && !n->is_CFG()) { - // Do 2 levels check and only data inputs. - bool no_dead_loop = true; - uint cnt = n->req(); - for (uint i = 1; i < cnt && no_dead_loop; i++) { - Node *in = n->in(i); - if (in == n) { - no_dead_loop = false; - } else if (in != nullptr && !in->is_dead_loop_safe()) { - uint icnt = in->req(); - for (uint j = 1; j < icnt && no_dead_loop; j++) { - if (in->in(j) == n || in->in(j) == in) - no_dead_loop = false; - } +void PhaseGVN::dead_loop_check(Node* n) { + // Phi may reference itself in a loop. + if (n == nullptr || n->is_dead_loop_safe() || n->is_CFG()) { + return; + } + + // Do 2 levels check and only data inputs. + for (uint i = 1; i < n->req(); i++) { + Node* in = n->in(i); + if (in == n) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node references itself: %s (%d)", + n->Name(), n->_idx); + } + + if (in == nullptr || in->is_dead_loop_safe()) { + continue; + } + for (uint j = 1; j < in->req(); j++) { + if (in->in(j) == n) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node input references current node: %s (%d) -> %s (%d)", + in->Name(), in->_idx, n->Name(), n->_idx); + } + if (in->in(j) == in) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node input references itself: %s (%d)", + in->Name(), in->_idx); } } - if (!no_dead_loop) { n->dump_bfs(100, nullptr, ""); } - assert(no_dead_loop, "dead loop detected"); } } diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 35b46d22732..31cc8fa0460 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -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 @@ -2482,6 +2482,11 @@ static bool can_subword_truncate(Node* in, const Type* type) { return false; } + // Since casts specifically change the type of a node, stay on the safe side and do not truncate them. + if (in->is_ConstraintCast()) { + return false; + } + // Cannot be truncated: switch (opc) { case Op_AbsI: diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index 02e9f82cfb6..f708e9c18ca 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -75,6 +75,7 @@ // v.release_store(x) -> void // v.release_store_fence(x) -> void // v.compare_exchange(x, y [, o]) -> T +// v.compare_set(x, y [, o]) -> bool // v.exchange(x [, o]) -> T // // (2) All atomic types are default constructible. @@ -267,6 +268,11 @@ public: return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order); } + bool compare_set(T compare_value, T new_value, + atomic_memory_order order = memory_order_conservative) { + return compare_exchange(compare_value, new_value, order) == compare_value; + } + T exchange(T new_value, atomic_memory_order order = memory_order_conservative) { return AtomicAccess::xchg(this->value_ptr(), new_value, order); @@ -479,6 +485,13 @@ public: order)); } + bool compare_set(T compare_value, T new_value, + atomic_memory_order order = memory_order_conservative) { + return _value.compare_set(decay(compare_value), + decay(new_value), + order); + } + T exchange(T new_value, atomic_memory_order order = memory_order_conservative) { return recover(_value.exchange(decay(new_value), order)); } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index c49a9f5d4b8..ee4f776df06 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.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 @@ -466,10 +466,7 @@ void before_exit(JavaThread* thread, bool halt) { event.commit(); } - // 2nd argument (emit_event_shutdown) should be set to false - // because EventShutdown would be emitted at Threads::destroy_vm(). - // (one of the callers of before_exit()) - JFR_ONLY(Jfr::on_vm_shutdown(true, false, halt);) + JFR_ONLY(Jfr::on_vm_shutdown(false, halt);) // Stop the WatcherThread. We do this before disenrolling various // PeriodicTasks to reduce the likelihood of races. diff --git a/src/hotspot/share/runtime/prefetch.inline.hpp b/src/hotspot/share/runtime/prefetch.inline.hpp index 4cc1d93f613..18630f71a62 100644 --- a/src/hotspot/share/runtime/prefetch.inline.hpp +++ b/src/hotspot/share/runtime/prefetch.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, 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 @@ -25,9 +25,24 @@ #ifndef SHARE_RUNTIME_PREFETCH_INLINE_HPP #define SHARE_RUNTIME_PREFETCH_INLINE_HPP -#include "runtime/prefetch.hpp" - +#include "memory/allStatic.hpp" #include "utilities/macros.hpp" + +// If calls to prefetch methods are in a loop, the loop should be cloned +// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval +// say not to do prefetching, these methods aren't called. At the very +// least, they take up a memory issue slot. They should be implemented +// as inline assembly code: doing an actual call isn't worth the cost. + +class Prefetch : AllStatic { + public: + // Prefetch anticipating read; must not fault, semantically a no-op + static void read(const void* loc, intx interval); + + // Prefetch anticipating write; must not fault, semantically a no-op + static void write(void* loc, intx interval); +}; + #include OS_CPU_HEADER_INLINE(prefetch) #endif // SHARE_RUNTIME_PREFETCH_INLINE_HPP diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index f9080364dc4..c478eda3e7c 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -191,8 +191,7 @@ // // C2 stub blob/field names // -// C2 stubs are provided with names in the format "C2 Runtime -// _blob". +// C2 stubs are provided with names in the format "_blob (C2 runtime)". // // A stub creation method OptoRuntime::generate(ciEnv* env) is // generated which invokes the C2 compiler to generate each stub in diff --git a/src/hotspot/share/runtime/stubInfo.cpp b/src/hotspot/share/runtime/stubInfo.cpp index 47a31fe7967..ee90631145a 100644 --- a/src/hotspot/share/runtime/stubInfo.cpp +++ b/src/hotspot/share/runtime/stubInfo.cpp @@ -479,7 +479,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_SHARED_BLOB(name, type) \ process_shared_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Shared Runtime " # name "_blob", \ + #name "_blob (shared runtime)", \ BlobId:: JOIN3(shared, name, id), \ StubId:: JOIN3(shared, name, id), \ EntryId:: JOIN3(shared, name, id), \ @@ -488,7 +488,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_C1_BLOB(name) \ process_c1_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "C1 Runtime " # name "_blob", \ + #name "_blob (C1 runtime)", \ BlobId:: JOIN3(c1, name, id), \ StubId:: JOIN3(c1, name, id), \ EntryId:: JOIN3(c1, name, id)); \ @@ -496,7 +496,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_C2_BLOB(name, type) \ process_c2_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "C2 Runtime " # name "_blob", \ + #name "_blob (C2 runtime)", \ BlobId:: JOIN3(c2, name, id), \ StubId:: JOIN3(c2, name, id), \ EntryId:: JOIN3(c2, name, id)); \ @@ -504,7 +504,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_C2_STUB(name, fancy_jump, pass_tls, return_pc) \ process_c2_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "C2 Runtime " # name "_blob", \ + #name "_blob (C2 runtime)", \ BlobId:: JOIN3(c2, name, id), \ StubId:: JOIN3(c2, name, id), \ EntryId:: JOIN3(c2, name, id)); \ @@ -512,20 +512,20 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_STUBGEN_BLOB(blob) \ process_stubgen_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # blob "_blob", \ + #blob "_blob (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id)); \ #define PROCESS_STUBGEN_STUB(blob, stub) \ process_stubgen_stub(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # stub "_stub", \ + #stub "_stub (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id)); \ #define PROCESS_STUBGEN_ENTRY(blob, stub, field_name, getter_name) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # field_name "_entry", \ + #field_name "_entry (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN3(stubgen, field_name, id), \ @@ -535,7 +535,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, init_funcion) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # field_name "_entry", \ + #field_name "_entry (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN3(stubgen, field_name, id), \ @@ -545,7 +545,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, count) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # field_name "_entry", \ + #field_name "_entry (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN3(stubgen, field_name, id), \ @@ -567,7 +567,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, init_function) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # arch_name "_" # field_name "_entry", \ + #arch_name "_" # field_name "_entry (stub gen)",\ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN4(stubgen, arch_name, \ diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index 31b451ba38a..62d2dd29dab 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -157,7 +157,7 @@ inline bool ConcurrentHashTable:: if (is_locked()) { return false; } - if (_first.compare_exchange(expect, node) == expect) { + if (_first.compare_set(expect, node)) { return true; } return false; @@ -172,7 +172,7 @@ inline bool ConcurrentHashTable:: } // We will expect a clean first pointer. Node* tmp = first(); - if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) { + if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) { return true; } return false; diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index de39fe32dc1..9e167141259 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.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 @@ -63,6 +63,9 @@ #include "utilities/nativeStackPrinter.hpp" #include "utilities/unsigned5.hpp" #include "utilities/vmError.hpp" +#if INCLUDE_JFR +#include "jfr/jfr.hpp" +#endif #include #include @@ -262,6 +265,8 @@ void report_untested(const char* file, int line, const char* message) { void report_java_out_of_memory(const char* message) { static int out_of_memory_reported = 0; + JFR_ONLY(Jfr::on_report_java_out_of_memory();) + // A number of threads may attempt to report OutOfMemoryError at around the // same time. To avoid dumping the heap or executing the data collection // commands multiple times we just do it once when the first threads reports diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index a290602e0be..88f81b31293 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2024 SAP SE. All rights reserved. * Copyright (c) 2023, 2025, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -1898,7 +1898,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt log.set_fd(-1); } - JFR_ONLY(Jfr::on_vm_shutdown(static_cast(_id) == OOM_JAVA_HEAP_FATAL, true);) + JFR_ONLY(Jfr::on_vm_shutdown(true, false, static_cast(_id) == OOM_JAVA_HEAP_FATAL);) if (PrintNMTStatistics) { fdStream fds(fd_out); diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp index b268b10c757..0892feab699 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.cpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.cpp @@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) { tag, waiters); int64_t new_state = encode(0, waiters); - if (_state.compare_exchange(state, new_state) == state) { + if (_state.compare_set(state, new_state)) { // Successfully disarmed. break; } @@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) { tag, waiters); int64_t new_state = encode(tag, waiters + 1); - if (_state.compare_exchange(state, new_state) == state) { + if (_state.compare_set(state, new_state)) { // Success! Proceed to wait. break; } @@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) { tag, waiters); int64_t new_state = encode(tag, waiters - 1); - if (_state.compare_exchange(state, new_state) == state) { + if (_state.compare_set(state, new_state)) { // Success! break; } diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 0f39ecf0a8a..55659bed57b 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2378,6 +2378,20 @@ public final class Math { */ @IntrinsicCandidate public static double fma(double a, double b, double c) { + // Implementation note: this method is intentionally coded in + // a straightforward manner relying on BigDecimal for the + // heavy-lifting of the numerical computation. It would be + // possible for the computation to be done solely using binary + // floating-point and integer operations, at the cost of more + // complicated logic. Since most processors have hardware + // support for fma and this method is an intrinsic candidate, + // the software implementation below would only be used on + // processors without native fma support (and also possibly on + // processors with native fma support while running in the + // interpreter). Therefore, the direct performance of the code + // is less of a concern than the code's simplicity, + // maintainability, and testability. + /* * Infinity and NaN arithmetic is not quite the same with two * roundings as opposed to just one so the simple expression @@ -2492,6 +2506,8 @@ public final class Math { */ @IntrinsicCandidate public static float fma(float a, float b, float c) { + // See implementation note in fma(double, double, double). + if (Float.isFinite(a) && Float.isFinite(b) && Float.isFinite(c)) { if (a == 0.0 || b == 0.0) { return a * b + c; // Handled signed zero cases diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index d3eda052740..1ac15e3a8b2 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -2045,19 +2045,26 @@ public final class String return encode(Charset.defaultCharset(), coder(), value); } - boolean bytesCompatible(Charset charset) { + boolean bytesCompatible(Charset charset, int srcIndex, int numChars) { if (isLatin1()) { if (charset == ISO_8859_1.INSTANCE) { return true; // ok, same encoding } else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) { - return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible + return !StringCoding.hasNegatives(value, srcIndex, numChars); // ok, if ASCII-compatible } } return false; } - void copyToSegmentRaw(MemorySegment segment, long offset) { - MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length); + void copyToSegmentRaw(MemorySegment segment, long offset, int srcIndex, int srcLength) { + if (!isLatin1()) { + // This method is intended to be used together with bytesCompatible, which currently only supports + // latin1 strings. In the future, bytesCompatible could be updated to handle more cases, like + // UTF-16 strings (when the platform and charset endianness match, and the String doesn’t contain + // unpaired surrogates). If that happens, copyToSegmentRaw should also be updated. + throw new IllegalStateException("This string does not support copyToSegmentRaw"); + } + MemorySegment.copy(value, srcIndex, segment, ValueLayout.JAVA_BYTE, offset, srcLength); } /** diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index f3a57c34165..cfe09c61375 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2331,13 +2331,13 @@ public final class System { } @Override - public void copyToSegmentRaw(String string, MemorySegment segment, long offset) { - string.copyToSegmentRaw(segment, offset); + public void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) { + string.copyToSegmentRaw(segment, offset, srcIndex, srcLength); } @Override - public boolean bytesCompatible(String string, Charset charset) { - return string.bytesCompatible(charset); + public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) { + return string.bytesCompatible(charset, srcIndex, numChars); } }); } diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java index 352f83f529c..4054a06c3e2 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java @@ -31,6 +31,8 @@ import java.lang.classfile.Instruction; import java.lang.classfile.Opcode; import jdk.internal.classfile.impl.AbstractInstruction; +import jdk.internal.classfile.impl.BytecodeHelpers; +import jdk.internal.classfile.impl.Util; /** * Models a local variable increment instruction in the {@code code} array of a @@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction * @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range */ static IncrementInstruction of(int slot, int constant) { - return new AbstractInstruction.UnboundIncrementInstruction(slot, constant); + var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC; + return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant); + } + + /** + * {@return an increment instruction} + *

+ * {@code slot} must be {@link java.lang.classfile##u1 u1} and + * {@code constant} must be within {@code [-128, 127]} for + * {@link Opcode#IINC iinc}, or {@code slot} must be + * {@link java.lang.classfile##u2 u2} and {@code constant} must be + * within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}. + * + * @apiNote + * The explicit {@code op} argument allows creating {@code wide} or + * regular increment instructions when {@code slot} and + * {@code constant} can be encoded with more optimized + * increment instructions. + * + * @param op the opcode for the specific type of increment instruction, + * which must be of kind {@link Opcode.Kind#INCREMENT} + * @param slot the local variable slot to increment + * @param constant the increment constant + * @throws IllegalArgumentException if the opcode kind is not + * {@link Opcode.Kind#INCREMENT} or {@code slot} or + * {@code constant} is out of range + * @since 27 + */ + static IncrementInstruction of(Opcode op, int slot, int constant) { + Util.checkKind(op, Opcode.Kind.INCREMENT); + BytecodeHelpers.validateIncrement(op, slot, constant); + return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant); } } diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 196f44d1abe..78098e39a17 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1296,12 +1296,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * over the decoding process is required. *

* Getting a string from a segment with a known byte offset and - * known byte length can be done like so: - * {@snippet lang=java : - * byte[] bytes = new byte[length]; - * MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, length); - * return new String(bytes, charset); - * } + * known byte length can be done using {@link #getString(long, Charset, long)}. * * @param offset offset in bytes (relative to this segment address) at which this * access operation will occur @@ -1328,6 +1323,41 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { */ String getString(long offset, Charset charset); + /** + * Reads a string from this segment at the given offset, using the provided length + * and charset. + *

+ * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + *

+ * If the string contains any {@code '\0'} characters, they will be read as well. + * This differs from {@link #getString(long, Charset)}, which will only read up + * to the first {@code '\0'}, resulting in truncation for string data that contains + * the {@code '\0'} character. + * + * @param offset offset in bytes (relative to this segment address) at which this + * access operation will occur + * @param charset the charset used to {@linkplain Charset#newDecoder() decode} the + * string bytes + * @param byteLength length, in bytes, of the region of memory to read and decode into + * a string + * @return a Java string constructed from the bytes read from the given starting + * address up to the given length + * @throws IllegalArgumentException if the size of the string is greater than the + * largest string supported by the platform + * @throws IndexOutOfBoundsException if {@code offset < 0} + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - byteLength} + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with + * this segment is not {@linkplain Scope#isAlive() alive} + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false} + * @throws IllegalArgumentException if {@code byteLength < 0} + * @since 27 + */ + String getString(long offset, Charset charset, long byteLength); + /** * Writes the given string into this segment at the given offset, converting it to * a null-terminated byte sequence using the {@linkplain StandardCharsets#UTF_8 UTF-8} @@ -1366,7 +1396,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * If the given string contains any {@code '\0'} characters, they will be * copied as well. This means that, depending on the method used to read * the string, such as {@link MemorySegment#getString(long)}, the string - * will appear truncated when read again. + * will appear truncated when read again. The string can be read without + * truncation using {@link #getString(long, Charset, long)}. * * @param offset offset in bytes (relative to this segment address) at which this * access operation will occur, the final address of this write @@ -2606,6 +2637,51 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { elementCount); } + /** + * Copies the byte sequence of the given string encoded using the provided charset + * to the destination segment. + *

+ * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + *

+ * If the given string contains any {@code '\0'} characters, they will be + * copied as well. This means that, depending on the method used to read + * the string, such as {@link MemorySegment#getString(long)}, the string + * will appear truncated when read again. The string can be read without + * truncation using {@link #getString(long, Charset, long)}. + * + * @param src the Java string to be written into the destination segment + * @param dstEncoding the charset used to {@linkplain Charset#newEncoder() encode} + * the string bytes. + * @param srcIndex the starting character index of the source string + * @param dst the destination segment + * @param dstOffset the starting offset, in bytes, of the destination segment + * @param numChars the number of characters to be copied + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with + * {@code dst} is not {@linkplain Scope#isAlive() alive} + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code dst.isAccessibleBy(T) == false} + * @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code numChars}, or {@code dstOffset} + * are {@code < 0} + * @throws IndexOutOfBoundsException if {@code srcIndex > src.length() - numChars} + * @throws IllegalArgumentException if {@code dst} is {@linkplain #isReadOnly() read-only} + * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size, + * in bytes, of the substring of {@code src} encoded using the given charset + * @return the number of copied bytes. + * @since 27 + */ + @ForceInline + static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) { + Objects.requireNonNull(src); + Objects.requireNonNull(dstEncoding); + Objects.requireNonNull(dst); + Objects.checkFromIndexSize(srcIndex, numChars, src.length()); + + return AbstractMemorySegmentImpl.copy(src, dstEncoding, srcIndex, dst, dstOffset, numChars); + } + /** * Finds and returns the relative offset, in bytes, of the first mismatch between the * source and the destination segments. More specifically, the bytes at offset diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java index 1297406dcf1..03d92ec24ef 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -111,7 +111,8 @@ public interface SegmentAllocator { * If the given string contains any {@code '\0'} characters, they will be * copied as well. This means that, depending on the method used to read * the string, such as {@link MemorySegment#getString(long)}, the string - * will appear truncated when read again. + * will appear truncated when read again. The string can be read without + * truncation using {@link MemorySegment#getString(long, Charset, long)}. * * @param str the Java string to be converted into a C string * @param charset the charset used to {@linkplain Charset#newEncoder() encode} the @@ -137,10 +138,10 @@ public interface SegmentAllocator { int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize(); MemorySegment segment; int length; - if (StringSupport.bytesCompatible(str, charset)) { + if (StringSupport.bytesCompatible(str, charset, 0, str.length())) { length = str.length(); segment = allocateNoInit((long) length + termCharSize); - StringSupport.copyToSegmentRaw(str, segment, 0); + StringSupport.copyToSegmentRaw(str, segment, 0, 0, str.length()); } else { byte[] bytes = str.getBytes(charset); length = bytes.length; @@ -153,6 +154,54 @@ public interface SegmentAllocator { return segment; } + /** + * Encodes a Java string using the provided charset and stores the resulting + * byte array into a memory segment. + *

+ * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement byte array. The + * {@link java.nio.charset.CharsetEncoder} class should be used when more + * control over the encoding process is required. + *

+ * If the given string contains any {@code '\0'} characters, they will be + * copied as well. This means that, depending on the method used to read + * the string, such as {@link MemorySegment#getString(long)}, the string + * will appear truncated when read again. The string can be read without + * truncation using {@link MemorySegment#getString(long, Charset, long)}. + * + * @param str the Java string to be encoded + * @param charset the charset used to {@linkplain Charset#newEncoder() encode} the + * string bytes + * @param srcIndex the starting index of the source string + * @param numChars the number of characters to be copied + * @return a new native segment containing the encoded string + * @throws IndexOutOfBoundsException if either {@code srcIndex} or {@code numChars} are {@code < 0} + * @throws IndexOutOfBoundsException if {@code srcIndex > str.length() - numChars} + * + * @implSpec The default implementation for this method copies the contents of the + * provided Java string into a new memory segment obtained by calling + * {@code this.allocate(B)}, where {@code B} is the size, in bytes, of + * the string encoded using the provided charset + * (e.g. {@code str.getBytes(charset).length}); + * @since 27 + */ + @ForceInline + default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) { + Objects.requireNonNull(charset); + Objects.requireNonNull(str); + Objects.checkFromIndexSize(srcIndex, numChars, str.length()); + MemorySegment segment; + if (StringSupport.bytesCompatible(str, charset, srcIndex, numChars)) { + segment = allocateNoInit(numChars); + StringSupport.copyToSegmentRaw(str, segment, 0, srcIndex, numChars); + } else { + byte[] bytes = str.substring(srcIndex, srcIndex + numChars).getBytes(charset); + segment = allocateNoInit(bytes.length); + MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length); + } + return segment; + } + /** * {@return a new memory segment initialized with the provided byte value} *

diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 6969fe8a8e1..30a22b05742 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -112,7 +113,7 @@ public final class Security { private static Path currentPath; - private static final Set activePaths = new HashSet<>(); + private static final List activePaths = new ArrayList<>(); static void loadAll() { // first load the master properties file to @@ -262,30 +263,40 @@ public final class Security { } } + private static void checkCyclicInclude(Path path) { + for (Path activePath : activePaths) { + try { + if (Files.isSameFile(path, activePath)) { + throw new InternalError( + "Cyclic include of '" + path + "'"); + } + } catch (IOException e) { + if (sdebug != null) { + sdebug.println("skipped exception when checking for " + + "cyclic inclusion of " + path + ":"); + e.printStackTrace(); + } + } + } + } + private static void loadFromPath(Path path, LoadingMode mode) throws IOException { - boolean isRegularFile = Files.isRegularFile(path); - if (isRegularFile) { - path = path.toRealPath(); - } else if (Files.isDirectory(path)) { + if (Files.isDirectory(path)) { throw new IOException("Is a directory"); - } else { - path = path.toAbsolutePath(); - } - if (activePaths.contains(path)) { - throw new InternalError("Cyclic include of '" + path + "'"); } try (InputStream is = Files.newInputStream(path)) { + checkCyclicInclude(path); reset(mode); Path previousPath = currentPath; - currentPath = isRegularFile ? path : null; + currentPath = Files.isRegularFile(path) ? path : null; activePaths.add(path); try { debugLoad(true, path); props.load(is); debugLoad(false, path); } finally { - activePaths.remove(path); + activePaths.removeLast(); currentPath = previousPath; } } 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 ab7ea53793f..ebcb9e3204c 100644 --- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 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 @@ -79,7 +79,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; @@ -190,12 +194,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 { + private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException { CheckedInputStream in = new CheckedInputStream(this_in, crc); crc.reset(); + + int magic; + if (!failOnEOF) { + // read an unsigned short value representing the GZIP magic header. + // this is the same as calling readUShort(in), except that here, + // when reading the first byte, we don't raise an EOFException + // if the stream has already reached EOF. + + // read unsigned byte + int b = in.read(); + if (b == -1) { // EOF + crc.reset(); + return -1; // represents no header bytes available + } + checkUnexpectedByte(b); + // read the next unsigned byte to form the unsigned + // short. we throw the usual EOFException/ZipException + // from this point on if there is no more data or + // the data doesn't represent a header. + magic = (readUByte(in) << 8) | b; + } else { + magic = readUShort(in); + } // Check header magic - if (readUShort(in) != GZIP_MAGIC) { + if (magic != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method @@ -261,7 +293,11 @@ public class GZIPInputStream extends InflaterInputStream { // try concatenated case int m = 8; // this.trailer try { - m += readHeader(in); // next.header + int numNextHeaderBytes = readHeader(in, false); // next.header (if available) + if (numNextHeaderBytes == -1) { + return true; // end of stream reached + } + m += numNextHeaderBytes; } catch (IOException ze) { return true; // ignore any malformed, do nothing } @@ -295,14 +331,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); + } + } + private byte[] tmpbuf = new byte[128]; /* diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index c5c45ca3553..b2a224f5917 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -634,10 +634,10 @@ public interface JavaLangAccess { /** * Copy the string bytes to an existing segment, avoiding intermediate copies. */ - void copyToSegmentRaw(String string, MemorySegment segment, long offset); + void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength); /** * Are the string bytes compatible with the given charset? */ - boolean bytesCompatible(String string, Charset charset); + boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 2197ac81e37..177103917de 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction final int slot; final int constant; - public UnboundIncrementInstruction(int slot, int constant) { - super(BytecodeHelpers.validateAndIsWideIinc(slot, constant) - ? Opcode.IINC_W - : Opcode.IINC); + public UnboundIncrementInstruction(Opcode op, int slot, int constant) { + super(op); this.slot = slot; this.constant = constant; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index a51728eb3e9..50c6f8b131c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -450,6 +450,13 @@ public class BytecodeHelpers { return ret; } + public static void validateIncrement(Opcode opcode, int slot, int constant) { + if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) { + throw new IllegalArgumentException( + "IINC: operands require wide encoding for %s".formatted(opcode)); + } + } + public static void validateRet(Opcode opcode, int slot) { if (opcode == Opcode.RET && (slot & ~0xFF) == 0 || opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0) diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index a0c8a0a5a4f..f75d67adbbb 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -551,6 +551,13 @@ public abstract sealed class AbstractMemorySegmentImpl unsafeGetOffset() == that.unsafeGetOffset(); } + @Override + public String getString(long offset, Charset charset, long byteLength) { + Utils.checkNonNegativeArgument(byteLength, "byteLength"); + Objects.requireNonNull(charset); + return StringSupport.read(this, offset, charset, byteLength); + } + @Override public int hashCode() { return Objects.hash( @@ -702,6 +709,16 @@ public abstract sealed class AbstractMemorySegmentImpl } } + @ForceInline + public static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) { + Objects.requireNonNull(src); + Objects.requireNonNull(dstEncoding); + Objects.requireNonNull(dst); + + AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dst; + return StringSupport.copyBytes(src, destImpl, dstEncoding, dstOffset, srcIndex, numChars); + } + // accessors @ForceInline diff --git a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java index 208c6d54aab..b9f528969f0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java +++ b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java @@ -30,11 +30,14 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.util.Architecture; import jdk.internal.util.ArraysSupport; +import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; import java.lang.foreign.MemorySegment; +import java.lang.reflect.Array; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; +import java.util.Objects; import static java.lang.foreign.ValueLayout.*; @@ -58,6 +61,27 @@ public final class StringSupport { }; } + @ForceInline + public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) { + return readBytes(segment, offset, charset, length); + } + + @ForceInline + public static String readBytes(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) { + if (length > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Required length exceeds implementation limit"); + } + final int lengthBytes = (int) length; + final byte[] bytes = new byte[lengthBytes]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, lengthBytes); + try { + return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); + } catch (CharacterCodingException _) { + // use replacement characters for malformed input + return new String(bytes, charset); + } + } + @ForceInline public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) { switch (CharsetKind.of(charset)) { @@ -70,14 +94,7 @@ public final class StringSupport { @ForceInline private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) { final int len = strlenByte(segment, offset, segment.byteSize()); - final byte[] bytes = new byte[len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); - try { - return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); - } catch (CharacterCodingException _) { - // use replacement characters for malformed input - return new String(bytes, charset); - } + return readBytes(segment, offset, charset, len); } @ForceInline @@ -89,14 +106,7 @@ public final class StringSupport { @ForceInline private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) { int len = strlenShort(segment, offset, segment.byteSize()); - byte[] bytes = new byte[len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); - try { - return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); - } catch (CharacterCodingException _) { - // use replacement characters for malformed input - return new String(bytes, charset); - } + return readBytes(segment, offset, charset, len); } @ForceInline @@ -108,14 +118,7 @@ public final class StringSupport { @ForceInline private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) { int len = strlenInt(segment, offset, segment.byteSize()); - byte[] bytes = new byte[len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); - try { - return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); - } catch (CharacterCodingException _) { - // use replacement characters for malformed input - return new String(bytes, charset); - } + return readBytes(segment, offset, charset, len); } @ForceInline @@ -345,22 +348,26 @@ public final class StringSupport { } } - public static boolean bytesCompatible(String string, Charset charset) { - return JAVA_LANG_ACCESS.bytesCompatible(string, charset); + public static boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) { + return JAVA_LANG_ACCESS.bytesCompatible(string, charset, srcIndex, numChars); } public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) { - if (bytesCompatible(string, charset)) { - copyToSegmentRaw(string, segment, offset); - return string.length(); + return copyBytes(string, segment, charset, offset, 0, string.length()); + } + + public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset, int srcIndex, int numChars) { + if (bytesCompatible(string, charset, srcIndex, numChars)) { + copyToSegmentRaw(string, segment, offset, srcIndex, numChars); + return numChars; } else { - byte[] bytes = string.getBytes(charset); + byte[] bytes = string.substring(srcIndex, srcIndex + numChars).getBytes(charset); MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length); return bytes.length; } } - public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) { - JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset); + public static void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) { + JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset, srcIndex, srcLength); } } diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 94395df2c34..92b9dbbef19 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.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 @@ -241,7 +241,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length] + * the range [0, array.length - 1] * @see #getBoolean(byte[], int) */ public static void setBoolean(byte[] array, int offset, boolean value) { @@ -328,7 +328,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloat(byte[], int) */ public static void setFloat(byte[] array, int offset, float value) { @@ -350,7 +350,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloatRaw(byte[], int) */ public static void setFloatRaw(byte[] array, int offset, float value) { @@ -368,7 +368,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] + * the range [0, array.length - 8] * @see #getLong(byte[], int) */ public static void setLong(byte[] array, int offset, long value) { @@ -387,7 +387,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDouble(byte[], int) */ public static void setDouble(byte[] array, int offset, double value) { @@ -409,7 +409,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDoubleRaw(byte[], int) */ public static void setDoubleRaw(byte[] array, int offset, double value) { diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java index fcd7ca2b9bf..cc50686429b 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.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 @@ -241,7 +241,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length] + * the range [0, array.length - 1] * @see #getBoolean(byte[], int) */ public static void setBoolean(byte[] array, int offset, boolean value) { @@ -328,7 +328,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloat(byte[], int) */ public static void setFloat(byte[] array, int offset, float value) { @@ -350,7 +350,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloatRaw(byte[], int) */ public static void setFloatRaw(byte[] array, int offset, float value) { @@ -368,7 +368,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] + * the range [0, array.length - 8] * @see #getLong(byte[], int) */ public static void setLong(byte[] array, int offset, long value) { @@ -387,7 +387,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDouble(byte[], int) */ public static void setDouble(byte[] array, int offset, double value) { @@ -409,7 +409,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDoubleRaw(byte[], int) */ public static void setDoubleRaw(byte[] array, int offset, double value) { diff --git a/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java b/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java index f58d7d8f2d7..7badcf42d9c 100644 --- a/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java +++ b/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,7 @@ public class ECDSAOperations { public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) { this.ecOps = ecOps; - this.basePoint = toAffinePoint(basePoint, ecOps.getField()); + this.basePoint = AffinePoint.fromECPoint(basePoint, ecOps.getField()); } public ECOperations getEcOperations() { @@ -79,14 +79,6 @@ public class ECDSAOperations { return ecOps.multiply(basePoint, scalar).asAffine(); } - public static AffinePoint toAffinePoint(ECPoint point, - IntegerFieldModuloP field) { - - ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX()); - ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY()); - return new AffinePoint(affineX, affineY); - } - public static Optional forParameters(ECParameterSpec ecParams) { Optional curveOps = diff --git a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java index 9c64402c123..44c9ed2d075 100644 --- a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.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 @@ -83,6 +83,22 @@ enum CADistrustPolicy { } CamerfirmaTLSPolicy.checkDistrust(chain); } + }, + + /** + * Distrust TLS Server certificates anchored by the Chunghwa ePKI root CA + * and issued after March 17, 2026. If enabled, this policy is currently + * enforced by the PKIX and SunX509 TrustManager implementations + * of the SunJSSE provider implementation. + */ + CHUNGHWA_TLS { + void checkDistrust(String variant, X509Certificate[] chain) + throws ValidatorException { + if (!variant.equals(Validator.VAR_TLS_SERVER)) { + return; + } + ChunghwaTLSPolicy.checkDistrust(chain); + } }; /** diff --git a/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java new file mode 100644 index 00000000000..114f5043fbf --- /dev/null +++ b/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.validator; + +import java.security.cert.X509Certificate; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import sun.security.util.Debug; +import sun.security.x509.X509CertImpl; + +/** + * This class checks if Chunghwa issued TLS Server certificates should be + * restricted. + */ +final class ChunghwaTLSPolicy { + + private static final Debug debug = Debug.getInstance("certpath"); + + // SHA-256 certificate fingerprint of distrusted root for TLS + // cacerts alias: chunghwaepkirootca + // DN: OU=ePKI Root Certification Authority, + // O="Chunghwa Telecom Co., Ltd.", C=TW + private static final String FINGERPRINT = + "C0A6F4DC63A24BFDCF54EF2A6A082A0A72DE35803E2FF5FF527AE5D87206DFD5"; + + // Any TLS Server certificate that is anchored by the Chunghwa + // root above and is issued after this date will be distrusted. + private static final LocalDate MARCH_17_2026 = + LocalDate.of(2026, Month.MARCH, 17); + + /** + * This method assumes the eeCert is a TLS Server Cert and chains back to + * the anchor. + * + * @param chain the end-entity's certificate chain. The end entity cert + * is at index 0, the trust anchor at index n-1. + * @throws ValidatorException if the certificate is distrusted + */ + static void checkDistrust(X509Certificate[] chain) + throws ValidatorException { + X509Certificate anchor = chain[chain.length-1]; + String fp = fingerprint(anchor); + if (fp == null) { + throw new ValidatorException("Cannot generate fingerprint for " + + "trust anchor of TLS server certificate"); + } + if (FINGERPRINT.equalsIgnoreCase(fp)) { + Date notBefore = chain[0].getNotBefore(); + LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(), + ZoneOffset.UTC); + // reject if certificate is issued after March 17, 2026 + checkNotBefore(ldNotBefore, MARCH_17_2026, anchor); + } + } + + private static String fingerprint(X509Certificate cert) { + return X509CertImpl.getFingerprint("SHA-256", cert, debug); + } + + // Check whether the certificate's notBeforeDate is after the + // distrust date for the anchor (root CA). Throw ValidatorException + // if it is after the distrust date. + private static void checkNotBefore(LocalDate notBeforeDate, + LocalDate distrustDate, X509Certificate anchor) + throws ValidatorException { + if (notBeforeDate.isAfter(distrustDate)) { + throw new ValidatorException + ("TLS Server certificate issued after " + distrustDate + + " and anchored by a distrusted legacy Chunghwa root CA: " + + anchor.getSubjectX500Principal(), + ValidatorException.T_UNTRUSTED_CERT, anchor); + } + } + + private ChunghwaTLSPolicy() {} +} diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 3d26c1dcd96..b5cbce413b2 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1437,6 +1437,9 @@ jdk.sasl.disabledMechanisms= # CAMERFIRMA_TLS : Distrust TLS Server certificates anchored by # a Camerfirma root CA and issued after April 15, 2025. # +# CHUNGHWA_TLS : Distrust TLS Server certificates anchored by +# a Chunghwa root CA and issued after March 17, 2026. +# # Leading and trailing whitespace surrounding each value are ignored. # Unknown values are ignored. If the property is commented out or set to the # empty String, no policies are enforced. @@ -1448,7 +1451,8 @@ jdk.sasl.disabledMechanisms= # jdk.certpath.disabledAlgorithms; those restrictions are still enforced even # if this property is not enabled. # -jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS +jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS,\ + CHUNGHWA_TLS # # FilePermission path canonicalization diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 30661a3f387..4343df663e6 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ To launch a source-file program: : Specifies the name of the class to be launched. Command-line entries following `classname` are the arguments for the main method. -`-jar` *jarfile* +[`-jar`]{#-jar} *jarfile* : Executes a program encapsulated in a JAR file. The *jarfile* argument is the name of a JAR file with a manifest that contains a line in the form `Main-Class:`*classname* that defines the class with the @@ -70,7 +70,7 @@ To launch a source-file program: is the source of all user classes, and other class path settings are ignored. If you're using JAR files, then see [jar](jar.html). -`-m` or `--module` *module*\[`/`*mainclass*\] +[`-m`]{#-m} or `--module` *module*\[`/`*mainclass*\] : Executes the main class in a module specified by *mainclass* if it is given, or, if it is not given, the value in the *module*. In other words, *mainclass* can be used when it is not specified by the module, or to @@ -370,7 +370,7 @@ the JVM. > **Note:** To specify an argument for a long option, you can use either `--`*name*`=`*value* or `--`*name* *value*. -`-agentlib:`*libname*\[`=`*options*\] +[`-agentlib:`]{#-agentlib}*libname*\[`=`*options*\] : Loads the specified native agent library. After the library name, a comma-separated list of options specific to the library can be used. If the option `-agentlib:foo` is specified, then the JVM attempts to @@ -393,12 +393,12 @@ the JVM. > `-agentlib:jdwp=transport=dt_socket,server=y,address=8000` -`-agentpath:`*pathname*\[`=`*options*\] +[`-agentpath:`]{#-agentpath}*pathname*\[`=`*options*\] : Loads the native agent library specified by the absolute path name. This option is equivalent to `-agentlib` but uses the full path and file name of the library. -`--class-path` *classpath*, `-classpath` *classpath*, or `-cp` *classpath* +[`--class-path`]{#--class-path} *classpath*, `-classpath` *classpath*, or `-cp` *classpath* : Specifies a list of directories, JAR files, and ZIP archives to search for class files. @@ -424,15 +424,15 @@ the JVM. expanded except by querying the environment, such as by calling `System.getenv("CLASSPATH")`. -`--disable-@files` +[`--disable-@files`]{#--disable-@files} : Can be used anywhere on the command line, including in an argument file, to prevent further `@filename` expansion. This option stops expanding `@`-argfiles after the option. -`--enable-preview` +[`--enable-preview`]{#--enable-preview} : Allows classes to depend on [preview features](https://docs.oracle.com/en/java/javase/12/language/index.html#JSLAN-GUID-5A82FE0E-0CA4-4F1F-B075-564874FE2823) of the release. -`--enable-native-access` *module*\[`,`*module*...\] +[`--enable-native-access`]{#--enable-native-access} *module*\[`,`*module*...\] : Native access involves access to code or data outside the Java runtime. This is generally unsafe and, if done incorrectly, might crash the JVM or result in memory corruption. Native access can occur as a result of calling a method that @@ -465,7 +465,7 @@ the JVM. run it with `--illegal-native-access=deny` along with any necessary `--enable-native-access` options. -`--enable-final-field-mutation` *module*\[,*module*...\] +[`--enable-final-field-mutation`]{#--enable-final-field-mutation} *module*\[,*module*...\] : Mutation of final fields is possible with the reflection API of the Java Platform. However, it compromises safety and performance in all programs. This option allows code in the specified modules to mutate final fields by reflection. @@ -498,13 +498,13 @@ the JVM. run it with `--illegal-final-field-mutation=deny` along with any necessary `--enable-final-field-mutation` options. -`--finalization=`*value* +[`--finalization=`]{#--finalization}*value* : Controls whether the JVM performs finalization of objects. Valid values are "enabled" and "disabled". Finalization is enabled by default, so the value "enabled" does nothing. The value "disabled" disables finalization, so that no finalizers are invoked. -`--module-path` *modulepath*... or `-p` *modulepath* +[`--module-path`]{#--module-path} *modulepath*... or `-p` *modulepath* : Specifies where to find application modules with a list of path elements. The elements of a module path can be a file path to a module or a directory containing modules. Each module is either a modular JAR or an @@ -513,7 +513,7 @@ the JVM. On Windows, semicolons (`;`) separate path elements in this list; on other platforms it is a colon (`:`). -`--upgrade-module-path` *modulepath*... +[`--upgrade-module-path`]{#--upgrade-module-path} *modulepath*... : Specifies where to find module replacements of upgradeable modules in the runtime image with a list of path elements. The elements of a module path can be a file path to a module or a directory @@ -523,33 +523,33 @@ the JVM. On Windows, semicolons (`;`) separate path elements in this list; on other platforms it is a colon (`:`). -`--add-modules` *module*\[`,`*module*...\] +[`--add-modules`]{#--add-modules} *module*\[`,`*module*...\] : Specifies the root modules to resolve in addition to the initial module. *module* can also be `ALL-DEFAULT`, `ALL-SYSTEM`, and `ALL-MODULE-PATH`. -`--list-modules` +[`--list-modules`]{#--list-modules} : Lists the observable modules and then exits. -`-d` *module\_name* or `--describe-module` *module\_name* +[`-d`]{#-d} *module\_name* or `--describe-module` *module\_name* : Describes a specified module and then exits. -`--dry-run` +[`--dry-run`]{#--dry-run} : Creates the VM but doesn't execute the main method. This `--dry-run` option might be useful for validating the command-line options such as the module system configuration. -`--validate-modules` +[`--validate-modules`]{#--validate-modules} : Validates all modules and exit. This option is helpful for finding conflicts and other errors with modules on the module path. -`-D`*property*`=`*value* +[`-D`]{#-D}*property*`=`*value* : Sets a system property value. The *property* variable is a string with no spaces that represents the name of the property. The *value* variable is a string that represents the value of the property. If *value* is a string with spaces, then enclose it in quotation marks (for example `-Dfoo="foo bar"`). -`-disableassertions`\[`:`\[*packagename*\]...\|`:`*classname*\] or `-da`\[`:`\[*packagename*\]...\|`:`*classname*\] +[`-disableassertions`]{#-disableassertions}\[`:`\[*packagename*\]...\|`:`*classname*\] or `-da`\[`:`\[*packagename*\]...\|`:`*classname*\] : Disables assertions. By default, assertions are disabled in all packages and classes. With no arguments, `-disableassertions` (`-da`) disables assertions in all packages and classes. With the *packagename* argument @@ -574,10 +574,10 @@ the JVM. > `java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat MyClass` -`-disablesystemassertions` or `-dsa` +[`-disablesystemassertions`]{#-disablesystemassertions} or `-dsa` : Disables assertions in all system classes. -`-enableassertions`\[`:`\[*packagename*\]...\|`:`*classname*\] or `-ea`\[`:`\[*packagename*\]...\|`:`*classname*\] +[`-enableassertions`]{#-enableassertions}\[`:`\[*packagename*\]...\|`:`*classname*\] or `-ea`\[`:`\[*packagename*\]...\|`:`*classname*\] : Enables assertions. By default, assertions are disabled in all packages and classes. With no arguments, `-enableassertions` (`-ea`) enables assertions in all packages and classes. With the *packagename* argument ending in @@ -604,28 +604,28 @@ the JVM. > `java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat MyClass` -`-enablesystemassertions` or `-esa` +[`-enablesystemassertions`]{#-enablesystemassertions} or `-esa` : Enables assertions in all system classes. -`-help`, `-h`, or `-?` +[`-help`]{#-help}, `-h`, or `-?` : Prints the help message to the error stream. -`--help` +[`--help`]{#--help} : Prints the help message to the output stream. -`-javaagent:`*jarpath*\[`=`*options*\] +[`-javaagent:`]{#-javaagent_}*jarpath*\[`=`*options*\] : Loads the specified Java programming language agent. See `java.lang.instrument`. -`--show-version` +[`--show-version`]{#--show-version} : Prints the product version to the output stream and continues. -`-showversion` +[`-showversion`]{#-showversion} : Prints the product version to the error stream and continues. -`--show-module-resolution` +[`--show-module-resolution`]{#--show-module-resolution} : Shows module resolution output during startup. -`-splash:`*imagepath* +[`-splash:`]{#-splash_}*imagepath* : Shows the splash screen with the image specified by *imagepath*. HiDPI scaled images are automatically supported and used if available. The unscaled image file name, such as `image.ext`, should always be passed as @@ -639,29 +639,29 @@ the JVM. See the SplashScreen API documentation for more information. -`-verbose:class` +[`-verbose:class`]{#-verbose_class} : Displays information about each loaded class. -`-verbose:gc` +[`-verbose:gc`]{#-verbose_gc} : Displays information about each garbage collection (GC) event. -`-verbose:jni` +[`-verbose:jni`]{#-verbose_jni} : Displays information about the use of native methods and other Java Native Interface (JNI) activity. -`-verbose:module` +[`-verbose:module`]{#-verbose_module} : Displays information about the modules in use. -`--version` +[`--version`]{#--version} : Prints product version to the output stream and exits. -`-version` +[`-version`]{#-version} : Prints product version to the error stream and exits. -`-X` +[`-X`]{#-X} : Prints the help on extra options to the error stream. -`--help-extra` +[`--help-extra`]{#--help-extra} : Prints the help on extra options to the output stream. `@`*argfile* @@ -688,7 +688,7 @@ the JVM. The following `java` options are general purpose options that are specific to the Java HotSpot Virtual Machine. -`-Xbatch` +[`-Xbatch`]{#-Xbatch} : Disables background compilation. By default, the JVM compiles the method as a background task, running the method in interpreter mode until the background compilation is finished. The `-Xbatch` flag disables background @@ -696,14 +696,14 @@ the Java HotSpot Virtual Machine. task until completed. This option is equivalent to `-XX:-BackgroundCompilation`. -`-Xbootclasspath/a:`*directories*\|*zip*\|*JAR-files* +[`-Xbootclasspath/a:`]{#-Xbootclasspath}*directories*\|*zip*\|*JAR-files* : Specifies a list of directories, JAR files, and ZIP archives to append to the end of the default bootstrap class path. On Windows, semicolons (`;`) separate entities in this list; on other platforms it is a colon (`:`). -`-Xcheck:jni` +[`-Xcheck:jni`]{#-Xcheck_jni} : Performs additional checks for Java Native Interface (JNI) functions. The following checks are considered indicative of significant problems @@ -739,22 +739,22 @@ the Java HotSpot Virtual Machine. Expect a performance degradation when this option is used. -`-Xcomp` +[`-Xcomp`]{#-Xcomp} : Testing mode to exercise JIT compilers. This option should not be used in production environments. -`-Xdebug` +[`-Xdebug`]{#-Xdebug} : Does nothing; deprecated for removal in a future release. -`-Xdiag` +[`-Xdiag`]{#-Xdiag} : Shows additional diagnostic messages. -`-Xint` +[`-Xint`]{#-Xint} : Runs the application in interpreted-only mode. Compilation to native code is disabled, and all bytecode is executed by the interpreter. The performance benefits offered by the just-in-time (JIT) compiler aren't present in this mode. -`-Xinternalversion` +[`-Xinternalversion`]{#-Xinternalversion} : Displays more detailed JVM version information than the `-version` option, and then exits. @@ -763,11 +763,11 @@ the Java HotSpot Virtual Machine. logging framework. See [Enable Logging with the JVM Unified Logging Framework]. -`-Xmixed` +[`-Xmixed`]{#-Xmixed} : Executes all bytecode by the interpreter except for hot methods, which are compiled to native code. On by default. Use `-Xint` to switch off. -`-Xmn` *size* +[`-Xmn`]{#-Xmn} *size* : Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery) in the generational collectors. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or @@ -793,7 +793,7 @@ the Java HotSpot Virtual Machine. the heap for the young generation, you can use `-XX:NewSize` to set the initial size and `-XX:MaxNewSize` to set the maximum size. -`-Xms` *size* +[`-Xms`]{#-Xms} *size* : Sets the minimum and the initial size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -815,7 +815,7 @@ the Java HotSpot Virtual Machine. initial heap size. If it appears after `-Xms` on the command line, then the initial heap size gets set to the value specified with `-XX:InitialHeapSize`. -`-Xmx` *size* +[`-Xmx`]{#-Xmx} *size* : Specifies the maximum size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 2 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -832,7 +832,7 @@ the Java HotSpot Virtual Machine. The `-Xmx` option is equivalent to `-XX:MaxHeapSize`. -`-Xnoclassgc` +[`-Xnoclassgc`]{#-Xnoclassgc} : Disables garbage collection (GC) of classes. This can save some GC time, which shortens interruptions during the application run. When you specify `-Xnoclassgc` at startup, the class objects in the application are left @@ -840,7 +840,7 @@ the Java HotSpot Virtual Machine. more memory being permanently occupied which, if not used carefully, throws an out-of-memory exception. -`-Xrs` +[`-Xrs`]{#-Xrs} : Reduces the use of operating system signals by the JVM. Shutdown hooks enable the orderly shutdown of a Java application by running user cleanup code (such as closing database connections) at shutdown, even if the JVM @@ -890,7 +890,7 @@ the Java HotSpot Virtual Machine. User code is responsible for causing shutdown hooks to run, for example, by calling `System.exit()` when the JVM is to be terminated. -`-Xshare:`*mode* +[`-Xshare:`]{#-Xshare_}*mode* : Sets the class data sharing (CDS) mode. Possible *mode* arguments for this option include the following: @@ -910,10 +910,10 @@ the Java HotSpot Virtual Machine. `off` : Do not attempt to use shared class data. -`-XshowSettings` +[`-XshowSettings`]{#-XshowSettings} : Shows all settings and then continues. -`-XshowSettings:`*category* +[`-XshowSettings:`]{#-XshowSettings_}*category* : Shows settings and continues. Possible *category* arguments for this option include the following: @@ -942,7 +942,7 @@ the Java HotSpot Virtual Machine. `system` : **Linux only:** Shows host system or container configuration and continues. -`-Xss` *size* +[`-Xss`]{#-Xss} *size* : Sets the thread stack size (in bytes). Append the letter `k` or `K` to indicate KB, `m` or `M` to indicate MB, or `g` or `G` to indicate GB. The actual size may be rounded up to a multiple of the system page size as @@ -970,32 +970,32 @@ the Java HotSpot Virtual Machine. This option is similar to `-XX:ThreadStackSize`. -`--add-reads` *module*`=`*target-module*(`,`*target-module*)\* +[`--add-reads`]{#--add-reads} *module*`=`*target-module*(`,`*target-module*)\* : Updates *module* to read the *target-module*, regardless of the module declaration. *target-module* can be `ALL-UNNAMED` to read all unnamed modules. -`--add-exports` *module*`/`*package*`=`*target-module*(`,`*target-module*)\* +[`--add-exports`]{#--add-exports} *module*`/`*package*`=`*target-module*(`,`*target-module*)\* : Updates *module* to export *package* to *target-module*, regardless of module declaration. *target-module* can be `ALL-UNNAMED` to export to all unnamed modules. -`--add-opens` *module*`/`*package*`=`*target-module*(`,`*target-module*)\* +[`--add-opens`]{#--add-opens} *module*`/`*package*`=`*target-module*(`,`*target-module*)\* : Updates *module* to open *package* to *target-module*, regardless of module declaration. -`--limit-modules` *module*\[`,`*module*...\] +[`--limit-modules`]{#--limit-modules} *module*\[`,`*module*...\] : Specifies the limit of the universe of observable modules. -`--patch-module` *module*`=`*file*(`;`*file*)\* +[`--patch-module`]{#--patch-module} *module*`=`*file*(`;`*file*)\* : Overrides or augments a module with classes and resources in JAR files or directories. -`--source` *version* +[`--source`]{#--source} *version* : Sets the version of the source in source-file mode. -`--sun-misc-unsafe-memory-access=` *value* +[`--sun-misc-unsafe-memory-access=`]{#--sun-misc-unsafe-memory-access} *value* : Allow or deny usage of unsupported API `sun.misc.Unsafe`. *value* is one of: `allow` @@ -1021,20 +1021,20 @@ the Java HotSpot Virtual Machine. The following extra options are macOS specific. -`-XstartOnFirstThread` +[`-XstartOnFirstThread`]{#-XstartOnFirstThread} : Runs the `main()` method on the first (AppKit) thread. -`-Xdock:name=`*application\_name* +[`-Xdock:name=`]{#-Xdock_name}*application\_name* : Overrides the default application name displayed in dock. -`-Xdock:icon=`*path\_to\_icon\_file* +[`-Xdock:icon=`]{#-Xdock_icon}*path\_to\_icon\_file* : Overrides the default icon displayed in dock. ## Advanced Options for Java These `java` options can be used to enable other advanced options. -`-XX:+UnlockDiagnosticVMOptions` +[`-XX:+UnlockDiagnosticVMOptions`]{#-XX__UnlockDiagnosticVMOptions} : Unlocks the options intended for diagnosing the JVM. By default, this option is disabled and diagnostic options aren't available. @@ -1046,7 +1046,7 @@ These `java` options can be used to enable other advanced options. of these options may be removed or their behavior changed without any warning. -`-XX:+UnlockExperimentalVMOptions` +[`-XX:+UnlockExperimentalVMOptions`]{#-XX__UnlockExperimentalVMOptions} : Unlocks the options that provide experimental features in the JVM. By default, this option is disabled and experimental features aren't available. @@ -1054,7 +1054,7 @@ These `java` options can be used to enable other advanced options. These `java` options control the runtime behavior of the Java HotSpot VM. -`-XX:ActiveProcessorCount=`*x* +[`-XX:ActiveProcessorCount=`]{#-XX_ActiveProcessorCount}*x* : Overrides the number of CPUs that the VM will use to calculate the size of thread pools it will use for various operations such as Garbage Collection and ForkJoinPool. @@ -1066,7 +1066,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. `-XX:-UseContainerSupport` for a description of enabling and disabling container support. -`-XX:AllocateHeapAt=`*path* +[`-XX:AllocateHeapAt=`]{#-XX_AllocateHeapAt}*path* : Takes a path to the file system and uses memory mapping to allocate the object heap on the memory device. Using this option enables the HotSpot VM to allocate the Java object heap on an alternative memory device, such as @@ -1084,7 +1084,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. The existing heap related flags (such as `-Xmx` and `-Xms`) and garbage-collection related flags continue to work as before. -`-XX:-CompactStrings` +[`-XX:-CompactStrings`]{#-XX__CompactStrings} : Disables the Compact Strings feature. By default, this option is enabled. When this option is enabled, Java Strings containing only single-byte characters are internally represented and stored as @@ -1107,7 +1107,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. In both of these scenarios, disabling Compact Strings makes sense. -`-XX:ErrorFile=`*filename* +[`-XX:ErrorFile=`]{#-XX_ErrorFile}*filename* : Specifies the path and file name to which error data is written when an irrecoverable error occurs. By default, this file is created in the current working directory and named `hs_err_pid`*pid*`.log` where *pid* is the @@ -1139,7 +1139,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. `TMP` environment variable; if that environment variable isn't defined, then the value of the `TEMP` environment variable is used. -`-XX:+ExtensiveErrorReports` +[`-XX:+ExtensiveErrorReports`]{#-XX__ExtensiveErrorReports} : Enables the reporting of more extensive error information in the `ErrorFile`. This option can be turned on in environments where maximal information is desired - even if the resulting logs may be quite large and/or contain @@ -1147,7 +1147,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. from release to release, and across different platforms. By default this option is disabled. -`-XX:FlightRecorderOptions=`*parameter*`=`*value* (or) `-XX:FlightRecorderOptions:`*parameter*`=`*value* +[`-XX:FlightRecorderOptions=`]{#-XX_FlightRecorderOptions}*parameter*`=`*value* (or) `-XX:FlightRecorderOptions:`*parameter*`=`*value* : Sets the parameters that control the behavior of JFR. Multiple parameters can be specified by separating them with a comma. @@ -1207,7 +1207,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. 4 kilobytes. Overriding this parameter could reduce performance and is not recommended. -`-XX:LargePageSizeInBytes=`*size* +[`-XX:LargePageSizeInBytes=`]{#-XX_LargePageSizeInBytes}*size* : Sets the maximum large page size (in bytes) used by the JVM. The *size* argument must be a valid page size supported by the environment to have any effect. Append the letter `k` or `K` to indicate kilobytes, @@ -1221,7 +1221,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. > `-XX:LargePageSizeInBytes=1g` -`-XX:MaxDirectMemorySize=`*size* +[`-XX:MaxDirectMemorySize=`]{#-XX_MaxDirectMemorySize}*size* : Sets the maximum total size (in bytes) of the `java.nio` package, direct-buffer allocations. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate @@ -1237,14 +1237,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. -XX:MaxDirectMemorySize=1048576 ``` -`-XX:-MaxFDLimit` +[`-XX:-MaxFDLimit`]{#-XX__MaxFDLimit} : Disables the attempt to set the soft limit for the number of open file descriptors to the hard limit. By default, this option is enabled on all platforms, but is ignored on Windows. The only time that you may need to disable this is on macOS, where its use imposes a maximum of 10240, which is lower than the actual system maximum. -`-XX:NativeMemoryTracking=`*mode* +[`-XX:NativeMemoryTracking=`]{#-XX_NativeMemoryTracking}*mode* : Specifies the mode for tracking JVM native memory usage. Possible *mode* arguments for this option include the following: @@ -1261,7 +1261,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. usage by individual `CallSite`, individual virtual memory region and its committed regions. -`-XX:TrimNativeHeapInterval=`*millis* +[`-XX:TrimNativeHeapInterval=`]{#-XX_TrimNativeHeapInterval}*millis* : Interval, in ms, at which the JVM will trim the native heap. Lower values will reclaim memory more eagerly at the cost of higher overhead. A value of 0 (default) disables native heap trimming. @@ -1269,7 +1269,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is only supported on Linux with GNU C Library (glibc). -`-XX:ObjectAlignmentInBytes=`*alignment* +[`-XX:ObjectAlignmentInBytes=`]{#-XX_ObjectAlignmentInBytes}*alignment* : Sets the memory alignment of Java objects (in bytes). By default, the value is set to 8 bytes. The specified value should be a power of 2, and must be within the range of 8 and 256 (inclusive). This option makes it possible to @@ -1283,7 +1283,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. increases. As a result, you may not realize any benefits from using compressed pointers with large Java heap sizes. -`-XX:OnError=`*string* +[`-XX:OnError=`]{#-XX_OnError}*string* : Sets a custom command or a series of semicolon-separated commands to run when an irrecoverable error occurs. If the string contains spaces, then it must be enclosed in quotation marks. @@ -1304,7 +1304,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. > `-XX:OnError="userdump.exe %p"` -`-XX:OnOutOfMemoryError=`*string* +[`-XX:OnOutOfMemoryError=`]{#-XX_OnOutOfMemoryError}*string* : Sets a custom command or a series of semicolon-separated commands to run when an `OutOfMemoryError` exception is first thrown by the JVM. If the string @@ -1316,31 +1316,31 @@ These `java` options control the runtime behavior of the Java HotSpot VM. directly from Java code, nor by the JVM for other types of resource exhaustion (such as native thread creation errors). -`-XX:+PrintCommandLineFlags` +[`-XX:+PrintCommandLineFlags`]{#-XX__PrintCommandLineFlags} : Enables printing of ergonomically selected JVM flags that appeared on the command line. It can be useful to know the ergonomic values set by the JVM, such as the heap space size and the selected garbage collector. By default, this option is disabled and flags aren't printed. -`-XX:+PreserveFramePointer` +[`-XX:+PreserveFramePointer`]{#-XX__PreserveFramePointer} : Selects between using the RBP register as a general purpose register (`-XX:-PreserveFramePointer`) and using the RBP register to hold the frame pointer of the currently executing method (`-XX:+PreserveFramePointer`). If the frame pointer is available, then external profiling tools (for example, Linux perf) can construct more accurate stack traces. -`-XX:+PrintNMTStatistics` +[`-XX:+PrintNMTStatistics`]{#-XX__PrintNMTStatistics} : Enables printing of collected native memory tracking data at JVM exit when native memory tracking is enabled (see `-XX:NativeMemoryTracking`). By default, this option is disabled and native memory tracking data isn't printed. -`-XX:SharedArchiveFile=`*path* +[`-XX:SharedArchiveFile=`]{#-XX_SharedArchiveFile}*path* : Specifies the path and name of the class data sharing (CDS) archive file See [Application Class Data Sharing]. -`-XX:+VerifySharedSpaces` +[`-XX:+VerifySharedSpaces`]{#-XX__VerifySharedSpaces} : If this option is specified, the JVM will load a CDS archive file only if it passes an integrity check based on CRC32 checksums. The purpose of this flag is to check for unintentional damage to CDS archive files in transmission or storage. @@ -1348,10 +1348,10 @@ These `java` options control the runtime behavior of the Java HotSpot VM. ensure that the CDS archive files used by Java applications cannot be modified without proper authorization. -`-XX:SharedArchiveConfigFile=`*shared\_config\_file* +[`-XX:SharedArchiveConfigFile=`]{#-XX_SharedArchiveConfigFile}*shared\_config\_file* : Specifies additional shared data added to the archive file. -`-XX:SharedClassListFile=`*file\_name* +[`-XX:SharedClassListFile=`]{#-XX_SharedClassListFile}*file\_name* : Specifies the text file that contains the names of the classes to store in the class data sharing (CDS) archive. This file contains the full name of one class per line, except slashes (`/`) replace dots (`.`). For example, @@ -1369,7 +1369,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. See [Application Class Data Sharing]. -`-XX:+ShowCodeDetailsInExceptionMessages` +[`-XX:+ShowCodeDetailsInExceptionMessages`]{#-XX__ShowCodeDetailsInExceptionMessages} : Enables printing of improved `NullPointerException` messages. When an application throws a `NullPointerException`, the option enables the JVM to analyze the program's bytecode instructions to determine precisely which reference is `null`, @@ -1378,13 +1378,13 @@ These `java` options control the runtime behavior of the Java HotSpot VM. and will be printed as the exception message along with the method, filename, and line number. By default, this option is enabled. -`-XX:+ShowMessageBoxOnError` +[`-XX:+ShowMessageBoxOnError`]{#-XX__ShowMessageBoxOnError} : Enables the display of a dialog box when the JVM experiences an irrecoverable error. This prevents the JVM from exiting and keeps the process active so that you can attach a debugger to it to investigate the cause of the error. By default, this option is disabled. -`-XX:StartFlightRecording:`*parameter*`=`*value* +[`-XX:StartFlightRecording:`]{#-XX_StartFlightRecording_}*parameter*`=`*value* : Starts a JFR recording for the Java application. This option is equivalent to the `JFR.start` diagnostic command that starts a recording during runtime. `-XX:StartFlightRecording:help` prints available options and @@ -1503,7 +1503,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. To only see warnings and errors from JFR during startup set -Xlog:jfr+startup=warning. -`-XX:ThreadStackSize=`*size* +[`-XX:ThreadStackSize=`]{#-XX_ThreadStackSize}*size* : Sets the Java thread stack size (in kilobytes). Use of a scaling suffix, such as `k`, results in the scaling of the kilobytes value so that `-XX:ThreadStackSize=1k` sets the Java thread stack size to 1024\*1024 @@ -1529,7 +1529,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is similar to `-Xss`. -`-XX:+UseCompactObjectHeaders` +[`-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. @@ -1538,7 +1538,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. In a future release it is expected to be enabled by default, and eventually will be the only mode of operation. -`-XX:-UseCompressedOops` +[`-XX:-UseCompressedOops`]{#-XX__UseCompressedOops} : Disables the use of compressed pointers. By default, this option is enabled, and compressed pointers are used. This will automatically limit the maximum ergonomically determined Java heap size to the maximum amount @@ -1553,7 +1553,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. It's possible to use compressed pointers with Java heap sizes greater than 32 GB. See the `-XX:ObjectAlignmentInBytes` option. -`-XX:-UseContainerSupport` +[`-XX:-UseContainerSupport`]{#-XX__UseContainerSupport} : **Linux only:** The VM now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers. It uses this @@ -1568,28 +1568,28 @@ These `java` options control the runtime behavior of the Java HotSpot VM. information. See [Enable Logging with the JVM Unified Logging Framework] for a description of using Unified Logging. -`-XX:+UseLargePages` +[`-XX:+UseLargePages`]{#-XX__UseLargePages} : Enables the use of large page memory. By default, this option is disabled and large page memory isn't used. See [Large Pages]. -`-XX:+UseTransparentHugePages` +[`-XX:+UseTransparentHugePages`]{#-XX__UseTransparentHugePages} : **Linux only:** Enables the use of large pages that can dynamically grow or shrink. This option is disabled by default. You may encounter performance problems with transparent huge pages as the OS moves other pages around to create huge pages; this option is made available for experimentation. -`-XX:+AllowUserSignalHandlers` +[`-XX:+AllowUserSignalHandlers`]{#-XX__AllowUserSignalHandlers} : **Non-Windows:** Enables installation of signal handlers by the application. By default, this option is disabled and the application isn't allowed to install signal handlers. -`-XX:VMOptionsFile=`*filename* +[`-XX:VMOptionsFile=`]{#-XX_VMOptionsFile}*filename* : Allows user to specify VM options in a file, for example, `java -XX:VMOptionsFile=/var/my_vm_options HelloWorld`. -`-XX:UseBranchProtection=`*mode* +[`-XX:UseBranchProtection=`]{#-XX_UseBranchProtection}*mode* : **Linux AArch64 only:** Specifies the branch protection mode. All options other than `none` require the VM to have been built with branch protection @@ -1613,14 +1613,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. These `java` options control the dynamic just-in-time (JIT) compilation performed by the Java HotSpot VM. -`-XX:AllocateInstancePrefetchLines=`*lines* +[`-XX:AllocateInstancePrefetchLines=`]{#-XX_AllocateInstancePrefetchLines}*lines* : Sets the number of lines to prefetch ahead of the instance allocation pointer. By default, the number of lines to prefetch is set to 1: > `-XX:AllocateInstancePrefetchLines=1` -`-XX:AllocatePrefetchDistance=`*size* +[`-XX:AllocatePrefetchDistance=`]{#-XX_AllocatePrefetchDistance}*size* : Sets the size (in bytes) of the prefetch distance for object allocation. Memory about to be written with the value of new objects is prefetched up to this distance starting from the address of the last allocated object. @@ -1636,7 +1636,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchDistance=1024` -`-XX:AllocatePrefetchInstr=`*instruction* +[`-XX:AllocatePrefetchInstr=`]{#-XX_AllocatePrefetchInstr}*instruction* : Sets the prefetch instruction to prefetch ahead of the allocation pointer. Possible values are from 0 to 3. The actual instructions behind the values depend on the platform. By default, the prefetch instruction is set to 0: @@ -1644,7 +1644,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchInstr=0` -`-XX:AllocatePrefetchLines=`*lines* +[`-XX:AllocatePrefetchLines=`]{#-XX_AllocatePrefetchLines}*lines* : Sets the number of cache lines to load after the last object allocation by using the prefetch instructions generated in compiled code. The default value is 1 if the last allocated object was an instance, and 3 if it was an @@ -1656,7 +1656,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchLines=5` -`-XX:AllocatePrefetchStepSize=`*size* +[`-XX:AllocatePrefetchStepSize=`]{#-XX_AllocatePrefetchStepSize}*size* : Sets the step size (in bytes) for sequential prefetch instructions. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, `g` or `G` to indicate gigabytes. By default, the step size is @@ -1665,7 +1665,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchStepSize=16` -`-XX:AllocatePrefetchStyle=`*style* +[`-XX:AllocatePrefetchStyle=`]{#-XX_AllocatePrefetchStyle}*style* : Sets the generated code style for prefetch instructions. The *style* argument is an integer from 0 to 3: @@ -1684,12 +1684,12 @@ performed by the Java HotSpot VM. : Generate one prefetch instruction per cache line. -`-XX:+BackgroundCompilation` +[`-XX:+BackgroundCompilation`]{#-XX__BackgroundCompilation} : Enables background compilation. This option is enabled by default. To disable background compilation, specify `-XX:-BackgroundCompilation` (this is equivalent to specifying `-Xbatch`). -`-XX:CICompilerCount=`*threads* +[`-XX:CICompilerCount=`]{#-XX_CICompilerCount}*threads* : Sets the number of compiler threads to use for compilation. By default, the number of compiler threads is selected automatically depending on the number of CPUs and memory available for compiled code. @@ -1697,11 +1697,11 @@ performed by the Java HotSpot VM. > `-XX:CICompilerCount=2` -`-XX:+UseDynamicNumberOfCompilerThreads` +[`-XX:+UseDynamicNumberOfCompilerThreads`]{#-XX__UseDynamicNumberOfCompilerThreads} : Dynamically create compiler thread up to the limit specified by `-XX:CICompilerCount`. This option is enabled by default. -`-XX:CompileCommand=`*command*`,`*method*\[`,`*option*\] +[`-XX:CompileCommand=`]{#-XX_CompileCommand}*command*`,`*method*\[`,`*option*\] : Specifies a *command* to perform on a *method*. For example, to exclude the `indexOf()` method of the `String` class from being compiled, use the following: @@ -1800,7 +1800,7 @@ performed by the Java HotSpot VM. You can suppress this by specifying the `-XX:CompileCommand=quiet` option before other `-XX:CompileCommand` options. -`-XX:CompileCommandFile=`*filename* +[`-XX:CompileCommandFile=`]{#-XX_CompileCommandFile}*filename* : Sets the file from which JIT compiler commands are read. By default, the `.hotspot_compiler` file is used to store commands performed by the JIT compiler. @@ -1814,7 +1814,7 @@ performed by the Java HotSpot VM. If you're using commands for the JIT compiler to perform on methods, then see the `-XX:CompileCommand` option. -`-XX:CompilerDirectivesFile=`*file* +[`-XX:CompilerDirectivesFile=`]{#-XX_CompilerDirectivesFile}*file* : Adds directives from a file to the directives stack when a program starts. See [Compiler Control](https://docs.oracle.com/en/java/javase/12/vm/compiler-control1.html#GUID-94AD8194-786A-4F19-BFFF-278F8E237F3A). @@ -1822,14 +1822,14 @@ performed by the Java HotSpot VM. `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:+CompilerDirectivesPrint` +[`-XX:+CompilerDirectivesPrint`]{#-XX__CompilerDirectivesPrint} : Prints the directives stack when the program starts or when a new directive is added. The `-XX:+CompilerDirectivesPrint` option has to be used together with the `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:CompileOnly=`*methods* +[`-XX:CompileOnly=`]{#-XX_CompileOnly}*methods* : Sets the list of methods (separated by commas) to which compilation should be restricted. Only the specified methods are compiled. @@ -1841,7 +1841,7 @@ performed by the Java HotSpot VM. -XX:CompileCommand=compileonly,methodN ``` -`-XX:CompileThresholdScaling=`*scale* +[`-XX:CompileThresholdScaling=`]{#-XX_CompileThresholdScaling}*scale* : Provides unified control of first compilation. This option controls when methods are first compiled for both the tiered and the nontiered modes of operation. The `CompileThresholdScaling` option has a floating point value @@ -1851,11 +1851,11 @@ performed by the Java HotSpot VM. compilation while values greater than 1.0 delay compilation. Setting `CompileThresholdScaling` to 0 is equivalent to disabling compilation. -`-XX:+DoEscapeAnalysis` +[`-XX:+DoEscapeAnalysis`]{#-XX__DoEscapeAnalysis} : Enables the use of escape analysis. This option is enabled by default. To disable the use of escape analysis, specify `-XX:-DoEscapeAnalysis`. -`-XX:InitialCodeCacheSize=`*size* +[`-XX:InitialCodeCacheSize=`]{#-XX_InitialCodeCacheSize}*size* : Sets the initial code cache size (in bytes). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. The default value depends on the platform. The initial code @@ -1865,11 +1865,11 @@ performed by the Java HotSpot VM. > `-XX:InitialCodeCacheSize=32k` -`-XX:+Inline` +[`-XX:+Inline`]{#-XX__Inline} : Enables method inlining. This option is enabled by default to increase performance. To disable method inlining, specify `-XX:-Inline`. -`-XX:InlineSmallCode=`*size* +[`-XX:InlineSmallCode=`]{#-XX_InlineSmallCode}*size* : Sets the maximum code size (in bytes) for already compiled methods that may be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, @@ -1879,7 +1879,7 @@ performed by the Java HotSpot VM. > `-XX:InlineSmallCode=1000` -`-XX:+LogCompilation` +[`-XX:+LogCompilation`]{#-XX__LogCompilation} : Enables logging of compilation activity to a file named `hotspot.log` in the current working directory. You can specify a different log file path and name using the `-XX:LogFile` option. @@ -1893,7 +1893,7 @@ performed by the Java HotSpot VM. `-XX:+PrintCompilation` option. -`-XX:FreqInlineSize=`*size* +[`-XX:FreqInlineSize=`]{#-XX_FreqInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a hot method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1903,7 +1903,7 @@ performed by the Java HotSpot VM. > `-XX:FreqInlineSize=325` -`-XX:MaxInlineSize=`*size* +[`-XX:MaxInlineSize=`]{#-XX_MaxInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a cold method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1912,7 +1912,7 @@ performed by the Java HotSpot VM. > `-XX:MaxInlineSize=35` -`-XX:C1MaxInlineSize=`*size* +[`-XX:C1MaxInlineSize=`]{#-XX_C1MaxInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a cold method to be inlined. This flag only applies to the C1 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1921,7 +1921,7 @@ performed by the Java HotSpot VM. > `-XX:MaxInlineSize=35` -`-XX:MaxTrivialSize=`*size* +[`-XX:MaxTrivialSize=`]{#-XX_MaxTrivialSize}*size* : Sets the maximum bytecode size (in bytes) of a trivial method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to @@ -1930,7 +1930,7 @@ performed by the Java HotSpot VM. > `-XX:MaxTrivialSize=6` -`-XX:C1MaxTrivialSize=`*size* +[`-XX:C1MaxTrivialSize=`]{#-XX_C1MaxTrivialSize}*size* : Sets the maximum bytecode size (in bytes) of a trivial method to be inlined. This flag only applies to the C1 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to @@ -1939,14 +1939,14 @@ performed by the Java HotSpot VM. > `-XX:MaxTrivialSize=6` -`-XX:MaxNodeLimit=`*nodes* +[`-XX:MaxNodeLimit=`]{#-XX_MaxNodeLimit}*nodes* : Sets the maximum number of nodes to be used during single method compilation. By default the value depends on the features enabled. In the following example the maximum number of nodes is set to 100,000: > `-XX:MaxNodeLimit=100000` -`-XX:NonNMethodCodeHeapSize=`*size* +[`-XX:NonNMethodCodeHeapSize=`]{#-XX_NonNMethodCodeHeapSize}*size* : Sets the size in bytes of the code segment containing nonmethod code. A nonmethod code segment containing nonmethod code, such as compiler @@ -1954,16 +1954,16 @@ performed by the Java HotSpot VM. cache forever. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:NonProfiledCodeHeapSize=`*size* +[`-XX:NonProfiledCodeHeapSize=`]{#-XX_NonProfiledCodeHeapSize}*size* : Sets the size in bytes of the code segment containing nonprofiled methods. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:+OptimizeStringConcat` +[`-XX:+OptimizeStringConcat`]{#-XX__OptimizeStringConcat} : Enables the optimization of `String` concatenation operations. This option is enabled by default. To disable the optimization of `String` concatenation operations, specify `-XX:-OptimizeStringConcat`. -`-XX:+PrintAssembly` +[`-XX:+PrintAssembly`]{#-XX__PrintAssembly} : Enables printing of assembly code for bytecoded and native methods by using the external `hsdis-.so` or `.dll` library. For 64-bit VM on Windows, it's `hsdis-amd64.dll`. This lets you to see the generated code, which may @@ -1973,11 +1973,11 @@ performed by the Java HotSpot VM. `-XX:+PrintAssembly` option has to be used together with the `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:ProfiledCodeHeapSize=`*size* +[`-XX:ProfiledCodeHeapSize=`]{#-XX_ProfiledCodeHeapSize}*size* : Sets the size in bytes of the code segment containing profiled methods. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:+PrintCompilation` +[`-XX:+PrintCompilation`]{#-XX__PrintCompilation} : Enables verbose diagnostic output from the JVM by printing a message to the console every time a method is compiled. This lets you to see which methods actually get compiled. By default, this option is disabled and diagnostic @@ -1986,7 +1986,7 @@ performed by the Java HotSpot VM. You can also log compilation activity to a file by using the `-XX:+LogCompilation` option. -`-XX:+PrintInlining` +[`-XX:+PrintInlining`]{#-XX__PrintInlining} : Enables printing of inlining decisions. This let's you see which methods are getting inlined. @@ -1995,7 +1995,7 @@ performed by the Java HotSpot VM. `-XX:+UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:ReservedCodeCacheSize=`*size* +[`-XX:ReservedCodeCacheSize=`]{#-XX_ReservedCodeCacheSize}*size* : Sets the maximum code cache size (in bytes) for JIT-compiled code. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. The default maximum code @@ -2005,7 +2005,7 @@ performed by the Java HotSpot VM. size shouldn't be less than the initial code cache size; see the option `-XX:InitialCodeCacheSize`. -`-XX:+SegmentedCodeCache` +[`-XX:+SegmentedCodeCache`]{#-XX__SegmentedCodeCache} : Enables segmentation of the code cache, without which the code cache consists of one large segment. With `-XX:+SegmentedCodeCache`, separate segments will be used for non-method, profiled method, and non-profiled @@ -2018,29 +2018,29 @@ performed by the Java HotSpot VM. (`-XX:+TieredCompilation` ) and the reserved code cache size (`-XX:ReservedCodeCacheSize`) is at least 240 MB. -`-XX:StartAggressiveSweepingAt=`*percent* +[`-XX:StartAggressiveSweepingAt=`]{#-XX_StartAggressiveSweepingAt}*percent* : Forces stack scanning of active methods to aggressively remove unused code when only the given percentage of the code cache is free. The default value is 10%. -`-XX:-TieredCompilation` +[`-XX:-TieredCompilation`]{#-XX__TieredCompilation} : Disables the use of tiered compilation. By default, this option is enabled. -`-XX:UseSSE=`*version* +[`-XX:UseSSE=`]{#-XX_UseSSE}*version* : Enables the use of SSE instruction set of a specified version. Is set by default to the highest supported version available (x86 only). -`-XX:UseAVX=`*version* +[`-XX:UseAVX=`]{#-XX_UseAVX}*version* : Enables the use of AVX instruction set of a specified version. Is set by default to the highest supported version available (x86 only). -`-XX:+UseAES` +[`-XX:+UseAES`]{#-XX__UseAES} : Enables hardware-based AES intrinsics for hardware that supports it. This option is on by default on hardware that has the necessary instructions. The `-XX:+UseAES` is used in conjunction with `UseAESIntrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAESIntrinsics` +[`-XX:+UseAESIntrinsics`]{#-XX__UseAESIntrinsics} : Enables AES intrinsics. Specifying `-XX:+UseAESIntrinsics` is equivalent to also enabling `-XX:+UseAES`. To disable hardware-based AES intrinsics, specify `-XX:-UseAES -XX:-UseAESIntrinsics`. For example, to enable hardware @@ -2051,47 +2051,47 @@ performed by the Java HotSpot VM. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAESCTRIntrinsics` +[`-XX:+UseAESCTRIntrinsics`]{#-XX__UseAESCTRIntrinsics} : Analogous to `-XX:+UseAESIntrinsics` enables AES/CTR intrinsics. -`-XX:+UseGHASHIntrinsics` +[`-XX:+UseGHASHIntrinsics`]{#-XX__UseGHASHIntrinsics} : Controls the use of GHASH intrinsics. Enabled by default on platforms that support the corresponding instructions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseChaCha20Intrinsics` +[`-XX:+UseChaCha20Intrinsics`]{#-XX__UseChaCha20Intrinsics} : Enable ChaCha20 intrinsics. This option is on by default for supported platforms. To disable ChaCha20 intrinsics, specify `-XX:-UseChaCha20Intrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UsePoly1305Intrinsics` +[`-XX:+UsePoly1305Intrinsics`]{#-XX__UsePoly1305Intrinsics} : Enable Poly1305 intrinsics. This option is on by default for supported platforms. To disable Poly1305 intrinsics, specify `-XX:-UsePoly1305Intrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseBASE64Intrinsics` +[`-XX:+UseBASE64Intrinsics`]{#-XX__UseBASE64Intrinsics} : Controls the use of accelerated BASE64 encoding routines for `java.util.Base64`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAdler32Intrinsics` +[`-XX:+UseAdler32Intrinsics`]{#-XX__UseAdler32Intrinsics} : Controls the use of Adler32 checksum algorithm intrinsic for `java.util.zip.Adler32`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCRC32Intrinsics` +[`-XX:+UseCRC32Intrinsics`]{#-XX__UseCRC32Intrinsics} : Controls the use of CRC32 intrinsics for `java.util.zip.CRC32`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCRC32CIntrinsics` +[`-XX:+UseCRC32CIntrinsics`]{#-XX__UseCRC32CIntrinsics} : Controls the use of CRC32C intrinsics for `java.util.zip.CRC32C`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA` +[`-XX:+UseSHA`]{#-XX__UseSHA} : Enables hardware-based intrinsics for SHA crypto hash functions for some hardware. The `UseSHA` option is used in conjunction with the `UseSHA1Intrinsics`, `UseSHA256Intrinsics`, and `UseSHA512Intrinsics` @@ -2108,26 +2108,26 @@ performed by the Java HotSpot VM. disable only a particular SHA intrinsic, use the appropriate corresponding option. For example: `-XX:-UseSHA256Intrinsics`. -`-XX:+UseSHA1Intrinsics` +[`-XX:+UseSHA1Intrinsics`]{#-XX__UseSHA1Intrinsics} : Enables intrinsics for SHA-1 crypto hash function. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA256Intrinsics` +[`-XX:+UseSHA256Intrinsics`]{#-XX__UseSHA256Intrinsics} : Enables intrinsics for SHA-224 and SHA-256 crypto hash functions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA512Intrinsics` +[`-XX:+UseSHA512Intrinsics`]{#-XX__UseSHA512Intrinsics} : Enables intrinsics for SHA-384 and SHA-512 crypto hash functions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseMathExactIntrinsics` +[`-XX:+UseMathExactIntrinsics`]{#-XX__UseMathExactIntrinsics} : Enables intrinsification of various `java.lang.Math.*Exact()` functions. Enabled by default. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseMultiplyToLenIntrinsic` +[`-XX:+UseMultiplyToLenIntrinsic`]{#-XX__UseMultiplyToLenIntrinsic} : Enables intrinsification of `BigInteger.multiplyToLen()`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. @@ -2152,44 +2152,44 @@ performed by the Java HotSpot VM. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCMoveUnconditionally` +[`-XX:+UseCMoveUnconditionally`]{#-XX__UseCMoveUnconditionally} : Generates CMove (scalar and vector) instructions regardless of profitability analysis. -`-XX:+UseCodeCacheFlushing` +[`-XX:+UseCodeCacheFlushing`]{#-XX__UseCodeCacheFlushing} : Enables flushing of the code cache before shutting down the compiler. This option is enabled by default. To disable flushing of the code cache before shutting down the compiler, specify `-XX:-UseCodeCacheFlushing`. -`-XX:+UseCondCardMark` +[`-XX:+UseCondCardMark`]{#-XX__UseCondCardMark} : Enables checking if the card is already marked before updating the card table. This option is disabled by default. It should be used only on machines with multiple sockets, where it increases the performance of Java applications that rely on concurrent operations. -`-XX:+UseCountedLoopSafepoints` +[`-XX:+UseCountedLoopSafepoints`]{#-XX__UseCountedLoopSafepoints} : Keeps safepoints in counted loops. Its default value depends on whether the selected garbage collector requires low latency safepoints. -`-XX:LoopStripMiningIter=`*number_of_iterations* +[`-XX:LoopStripMiningIter=`]{#-XX_LoopStripMiningIter}*number_of_iterations* : Controls the number of iterations in the inner strip mined loop. Strip mining transforms counted loops into two level nested loops. Safepoints are kept in the outer loop while the inner loop can execute at full speed. This option controls the maximum number of iterations in the inner loop. The default value is 1,000. -`-XX:LoopStripMiningIterShortLoop=`*number_of_iterations* +[`-XX:LoopStripMiningIterShortLoop=`]{#-XX_LoopStripMiningIterShortLoop}*number_of_iterations* : Controls loop strip mining optimization. Loops with the number of iterations less than specified will not have safepoints in them. Default value is 1/10th of `-XX:LoopStripMiningIter`. -`-XX:+UseFMA` +[`-XX:+UseFMA`]{#-XX__UseFMA} : Enables hardware-based FMA intrinsics for hardware where FMA instructions are available (such as, Intel and ARM64). FMA intrinsics are generated for the `java.lang.Math.fma(`*a*`,` *b*`,` *c*`)` methods that calculate the value of `(` *a* `*` *b* `+` *c* `)` expressions. -`-XX:+UseSuperWord` +[`-XX:+UseSuperWord`]{#-XX__UseSuperWord} : Enables the transformation of scalar operations into superword operations. Superword is a vectorization optimization. This option is enabled by default. To disable the transformation of scalar operations into superword @@ -2200,7 +2200,7 @@ performed by the Java HotSpot VM. These `java` options provide the ability to gather system information and perform extensive debugging. -`-XX:+DisableAttachMechanism` +[`-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 can use diagnostics and troubleshooting tools such as `jcmd`, `jstack`, @@ -2211,17 +2211,17 @@ perform extensive debugging. supported when using the tools from one JDK version to troubleshoot a different JDK version. -`-XX:+DTraceAllocProbes` +[`-XX:+DTraceAllocProbes`]{#-XX__DTraceAllocProbes} : **Linux and macOS:** Enable `dtrace` tool probes for object allocation. -`-XX:+DTraceMethodProbes` +[`-XX:+DTraceMethodProbes`]{#-XX__DTraceMethodProbes} : **Linux and macOS:** Enable `dtrace` tool probes for method-entry and method-exit. -`-XX:+DTraceMonitorProbes` +[`-XX:+DTraceMonitorProbes`]{#-XX__DTraceMonitorProbes} : **Linux and macOS:** Enable `dtrace` tool probes for monitor events. -`-XX:+HeapDumpOnOutOfMemoryError` +[`-XX:+HeapDumpOnOutOfMemoryError`]{#-XX__HeapDumpOnOutOfMemoryError} : Enables the dumping of the Java heap to a file in the current directory by using the heap profiler (HPROF) when a `java.lang.OutOfMemoryError` exception is thrown by the JVM. You can explicitly set the heap dump file path and @@ -2233,7 +2233,7 @@ perform extensive debugging. directly from Java code, nor by the JVM for other types of resource exhaustion (such as native thread creation errors). -`-XX:HeapDumpPath=`*path* +[`-XX:HeapDumpPath=`]{#-XX_HeapDumpPath}*path* : Sets the path and file name for writing the heap dump provided by the heap profiler (HPROF) when the `-XX:+HeapDumpOnOutOfMemoryError` option is set. By default, the file is created in the current working directory, and it's @@ -2253,7 +2253,7 @@ perform extensive debugging. > `-XX:HeapDumpPath=C:/log/java/java_heapdump.log` -`-XX:LogFile=`*path* +[`-XX:LogFile=`]{#-XX_LogFile}*path* : Sets the path and file name to where log data is written. By default, the file is created in the current working directory, and it's named `hotspot.log`. @@ -2268,7 +2268,7 @@ perform extensive debugging. > `-XX:LogFile=C:/log/java/hotspot.log` -`-XX:+PrintClassHistogram` +[`-XX:+PrintClassHistogram`]{#-XX__PrintClassHistogram} : Enables printing of a class instance histogram after one of the following events: @@ -2282,7 +2282,7 @@ perform extensive debugging. the `jcmd` *pid* `GC.class_histogram` command, where *pid* is the current Java process identifier. -`-XX:+PrintConcurrentLocks` +[`-XX:+PrintConcurrentLocks`]{#-XX__PrintConcurrentLocks} : Enables printing of `java.util.concurrent` locks after one of the following events: @@ -2296,12 +2296,12 @@ perform extensive debugging. `jcmd` *pid* `Thread.print -l` command, where *pid* is the current Java process identifier. -`-XX:+PrintFlagsRanges` +[`-XX:+PrintFlagsRanges`]{#-XX__PrintFlagsRanges} : Prints the range specified and allows automatic testing of the values. See [Validate Java Virtual Machine Flag Arguments]. -`-XX:+PerfDataSaveToFile` +[`-XX:+PerfDataSaveToFile`]{#-XX__PerfDataSaveToFile} : If enabled, saves [jstat](jstat.html) binary data when the Java application exits. This binary data is saved in a file named `hsperfdata_`*pid*, where *pid* is the process identifier of the Java application that you ran. Use @@ -2312,7 +2312,7 @@ perform extensive debugging. > `jstat -gc file:///`*path*`/hsperfdata_`*pid* -`-XX:+UsePerfData` +[`-XX:+UsePerfData`]{#-XX__UsePerfData} : Enables the `perfdata` feature. This option is enabled by default to allow JVM monitoring and performance testing. Disabling it suppresses the creation of the `hsperfdata_userid` directories. To disable the `perfdata` @@ -2323,13 +2323,13 @@ perform extensive debugging. These `java` options control how garbage collection (GC) is performed by the Java HotSpot VM. -`-XX:+AlwaysPreTouch` +[`-XX:+AlwaysPreTouch`]{#-XX__AlwaysPreTouch} : Requests the VM to touch every page on the Java heap after requesting it from the operating system and before handing memory out to the application. By default, this option is disabled and all pages are committed as the application uses the heap space. -`-XX:ConcGCThreads=`*threads* +[`-XX:ConcGCThreads=`]{#-XX_ConcGCThreads}*threads* : Sets the number of threads used for concurrent GC. Sets *`threads`* to approximately 1/4 of the number of parallel garbage collection threads. The default value depends on the number of CPUs available to the JVM. @@ -2339,24 +2339,24 @@ Java HotSpot VM. > `-XX:ConcGCThreads=2` -`-XX:+DisableExplicitGC` +[`-XX:+DisableExplicitGC`]{#-XX__DisableExplicitGC} : Enables the option that disables processing of calls to the `System.gc()` method. This option is disabled by default, meaning that calls to `System.gc()` are processed. If processing of calls to `System.gc()` is disabled, then the JVM still performs GC when necessary. -`-XX:+ExplicitGCInvokesConcurrent` +[`-XX:+ExplicitGCInvokesConcurrent`]{#-XX__ExplicitGCInvokesConcurrent} : Enables invoking of concurrent GC by using the `System.gc()` request. This option is disabled by default and can be enabled only with the `-XX:+UseG1GC` option. -`-XX:G1AdaptiveIHOPNumInitialSamples=`*number* +[`-XX:G1AdaptiveIHOPNumInitialSamples=`]{#-XX_G1AdaptiveIHOPNumInitialSamples}*number* : When `-XX:UseAdaptiveIHOP` is enabled, this option sets the number of completed marking cycles used to gather samples until G1 adaptively determines the optimum value of `-XX:InitiatingHeapOccupancyPercent`. Before, G1 uses the value of `-XX:InitiatingHeapOccupancyPercent` directly for this purpose. The default value is 3. -`-XX:G1HeapRegionSize=`*size* +[`-XX:G1HeapRegionSize=`]{#-XX_G1HeapRegionSize}*size* : Sets the size of the regions into which the Java heap is subdivided when using the garbage-first (G1) collector. The value is a power of 2 and can range from 1 MB to 32 MB. The default region size is determined @@ -2367,44 +2367,44 @@ Java HotSpot VM. > `-XX:G1HeapRegionSize=16m` -`-XX:G1HeapWastePercent=`*percent* +[`-XX:G1HeapWastePercent=`]{#-XX_G1HeapWastePercent}*percent* : Sets the percentage of heap that you're willing to waste. The Java HotSpot VM doesn't initiate the mixed garbage collection cycle when the reclaimable percentage is less than the heap waste percentage. The default is 5 percent. -`-XX:G1MaxNewSizePercent=`*percent* +[`-XX:G1MaxNewSizePercent=`]{#-XX_G1MaxNewSizePercent}*percent* : Sets the percentage of the heap size to use as the maximum for the young generation size. The default value is 60 percent of your Java heap. This is an experimental flag. This setting replaces the `-XX:DefaultMaxNewGenPercent` setting. -`-XX:G1MixedGCCountTarget=`*number* +[`-XX:G1MixedGCCountTarget=`]{#-XX_G1MixedGCCountTarget}*number* : Sets the target number of mixed garbage collections after a marking cycle to collect old regions with at most `G1MixedGCLIveThresholdPercent` live data. The default is 8 mixed garbage collections. The goal for mixed collections is to be within this target number. -`-XX:G1MixedGCLiveThresholdPercent=`*percent* +[`-XX:G1MixedGCLiveThresholdPercent=`]{#-XX_G1MixedGCLiveThresholdPercent}*percent* : Sets the occupancy threshold for an old region to be included in a mixed garbage collection cycle. The default occupancy is 85 percent. This is an experimental flag. This setting replaces the `-XX:G1OldCSetRegionLiveThresholdPercent` setting. -`-XX:G1NewSizePercent=`*percent* +[`-XX:G1NewSizePercent=`]{#-XX_G1NewSizePercent}*percent* : Sets the percentage of the heap to use as the minimum for the young generation size. The default value is 5 percent of your Java heap. This is an experimental flag. This setting replaces the `-XX:DefaultMinNewGenPercent` setting. -`-XX:G1OldCSetRegionThresholdPercent=`*percent* +[`-XX:G1OldCSetRegionThresholdPercent=`]{#-XX_G1OldCSetRegionThresholdPercent}*percent* : Sets an upper limit on the number of old regions to be collected during a mixed garbage collection cycle. The default is 10 percent of the Java heap. -`-XX:G1ReservePercent=`*percent* +[`-XX:G1ReservePercent=`]{#-XX_G1ReservePercent}*percent* : Sets the percentage of the heap (0 to 50) that's reserved as a false ceiling to reduce the possibility of promotion failure for the G1 collector. When you increase or decrease the percentage, ensure that you @@ -2415,7 +2415,7 @@ Java HotSpot VM. > `-XX:G1ReservePercent=20` -`-XX:+G1UseAdaptiveIHOP` +[`-XX:+G1UseAdaptiveIHOP`]{#-XX__G1UseAdaptiveIHOP} : Controls adaptive calculation of the old generation occupancy to start background work preparing for an old generation collection. If enabled, G1 uses `-XX:InitiatingHeapOccupancyPercent` for the first few times as @@ -2428,7 +2428,7 @@ Java HotSpot VM. The default is enabled. -`-XX:InitialHeapSize=`*size* +[`-XX:InitialHeapSize=`]{#-XX_InitialHeapSize}*size* : Sets the initial size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, @@ -2452,7 +2452,7 @@ Java HotSpot VM. command line, then the initial heap size gets set to the value specified with `-Xms`. -`-XX:InitialRAMPercentage=`*percent* +[`-XX:InitialRAMPercentage=`]{#-XX_InitialRAMPercentage}*percent* : Sets the initial amount of memory that the JVM will use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the `-XX:MaxRAM` option. @@ -2462,7 +2462,7 @@ Java HotSpot VM. > `-XX:InitialRAMPercentage=5` -`-XX:InitialSurvivorRatio=`*ratio* +[`-XX:InitialSurvivorRatio=`]{#-XX_InitialSurvivorRatio}*ratio* : Sets the initial survivor space ratio used by the throughput garbage collector (which is enabled by the `-XX:+UseParallelGC` option). Adaptive sizing is enabled by default with the throughput garbage collector by @@ -2491,7 +2491,7 @@ Java HotSpot VM. > `-XX:InitialSurvivorRatio=4` -`-XX:InitiatingHeapOccupancyPercent=`*percent* +[`-XX:InitiatingHeapOccupancyPercent=`]{#-XX_InitiatingHeapOccupancyPercent}*percent* : Sets the percentage of the old generation occupancy (0 to 100) at which to start the first few concurrent marking cycles for the G1 garbage collector. @@ -2506,7 +2506,7 @@ Java HotSpot VM. > `-XX:InitiatingHeapOccupancyPercent=75` -`-XX:MaxGCPauseMillis=`*time* +[`-XX:MaxGCPauseMillis=`]{#-XX_MaxGCPauseMillis}*time* : Sets a target for the maximum GC pause time (in milliseconds). This is a soft goal, and the JVM will make its best effort to achieve it. Only G1 and Parallel support a maximum GC pause time target. For G1, the default @@ -2517,7 +2517,7 @@ Java HotSpot VM. > `-XX:MaxGCPauseMillis=500` -`-XX:MaxHeapSize=`*size* +[`-XX:MaxHeapSize=`]{#-XX_MaxHeapSize}*size* : Sets the maximum size (in byes) of the memory allocation pool. This value must be a multiple of 1024 and greater than 2 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -2537,7 +2537,7 @@ Java HotSpot VM. The `-XX:MaxHeapSize` option is equivalent to `-Xmx`. -`-XX:MaxHeapFreeRatio=`*percent* +[`-XX:MaxHeapFreeRatio=`]{#-XX_MaxHeapFreeRatio}*percent* : Sets the maximum allowed percentage of free heap space (0 to 100) after a GC event. If free heap space expands above this value, then the heap is shrunk. By default, this value is set to 70%. @@ -2558,7 +2558,7 @@ Java HotSpot VM. description of using this option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:MaxMetaspaceSize=`*size* +[`-XX:MaxMetaspaceSize=`]{#-XX_MaxMetaspaceSize}*size* : Sets the maximum amount of native memory that can be allocated for class metadata. By default, the size isn't limited. The amount of metadata for an application depends on the application itself, other running applications, @@ -2569,11 +2569,11 @@ Java HotSpot VM. > `-XX:MaxMetaspaceSize=256m` -`-XX:MaxNewSize=`*size* +[`-XX:MaxNewSize=`]{#-XX_MaxNewSize}*size* : Sets the maximum size (in bytes) of the heap for the young generation (nursery). The default value is set ergonomically. -`-XX:MaxRAMPercentage=`*percent* +[`-XX:MaxRAMPercentage=`]{#-XX_MaxRAMPercentage}*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the `-XX:MaxRAM` option. The default value is 25 @@ -2589,7 +2589,7 @@ Java HotSpot VM. > `-XX:MaxRAMPercentage=75` -`-XX:MinRAMPercentage=`*percent* +[`-XX:MinRAMPercentage=`]{#-XX_MinRAMPercentage}*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the `-XX:MaxRAM` option for small heaps. A small @@ -2600,7 +2600,7 @@ Java HotSpot VM. > `-XX:MinRAMPercentage=75` -`-XX:MaxTenuringThreshold=`*threshold* +[`-XX:MaxTenuringThreshold=`]{#-XX_MaxTenuringThreshold}*threshold* : Sets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector. @@ -2610,13 +2610,13 @@ Java HotSpot VM. > `-XX:MaxTenuringThreshold=10` -`-XX:MetaspaceSize=`*size* +[`-XX:MetaspaceSize=`]{#-XX_MetaspaceSize}*size* : Sets the size of the allocated class metadata space that triggers a garbage collection the first time it's exceeded. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used. The default size depends on the platform. -`-XX:MinHeapFreeRatio=`*percent* +[`-XX:MinHeapFreeRatio=`]{#-XX_MinHeapFreeRatio}*percent* : Sets the minimum allowed percentage of free heap space (0 to 100) after a GC event. If free heap space falls below this value, then the heap is expanded. By default, this value is set to 40%. @@ -2637,7 +2637,7 @@ Java HotSpot VM. description of using this option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:MinHeapSize=`*size* +[`-XX:MinHeapSize=`]{#-XX_MinHeapSize}*size* : Sets the minimum size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, @@ -2656,14 +2656,14 @@ Java HotSpot VM. If you set this option to 0, then the minimum size is set to the same value as the initial size. -`-XX:NewRatio=`*ratio* +[`-XX:NewRatio=`]{#-XX_NewRatio}*ratio* : Sets the ratio between young and old generation sizes. By default, this option is set to 2. The following example shows how to set the young-to-old ratio to 1: > `-XX:NewRatio=1` -`-XX:NewSize=`*size* +[`-XX:NewSize=`]{#-XX_NewSize}*size* : Sets the initial size (in bytes) of the heap for the young generation (nursery). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. @@ -2687,7 +2687,7 @@ Java HotSpot VM. The `-XX:NewSize` option is equivalent to `-Xmn`. -`-XX:ParallelGCThreads=`*threads* +[`-XX:ParallelGCThreads=`]{#-XX_ParallelGCThreads}*threads* : Sets the number of the stop-the-world (STW) worker threads. The default value depends on the number of CPUs available to the JVM and the garbage collector selected. @@ -2697,11 +2697,11 @@ Java HotSpot VM. > `-XX:ParallelGCThreads=2` -`-XX:+PrintAdaptiveSizePolicy` +[`-XX:+PrintAdaptiveSizePolicy`]{#-XX__PrintAdaptiveSizePolicy} : Enables printing of information about adaptive-generation sizing. By default, this option is disabled. -`-XX:SoftRefLRUPolicyMSPerMB=`*time* +[`-XX:SoftRefLRUPolicyMSPerMB=`]{#-XX_SoftRefLRUPolicyMSPerMB}*time* : Sets the amount of time (in milliseconds) a softly reachable object is kept active on the heap after the last time it was referenced. The default value is one second of lifetime per free megabyte in the heap. The @@ -2718,7 +2718,7 @@ Java HotSpot VM. `-XX:SoftRefLRUPolicyMSPerMB=2500` -`-XX:-ShrinkHeapInSteps` +[`-XX:-ShrinkHeapInSteps`]{#-XX__ShrinkHeapInSteps} : Incrementally reduces the Java heap to the target size, specified by the option `-XX:MaxHeapFreeRatio`. This option is enabled by default. If disabled, then it immediately reduces the Java heap to the target size @@ -2730,7 +2730,7 @@ Java HotSpot VM. `MaxHeapFreeRatio` option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:StringDeduplicationAgeThreshold=`*threshold* +[`-XX:StringDeduplicationAgeThreshold=`]{#-XX_StringDeduplicationAgeThreshold}*threshold* : Identifies `String` objects reaching the specified age that are considered candidates for deduplication. An object's age is a measure of how many times it has survived garbage collection. This is sometimes referred to as @@ -2741,14 +2741,14 @@ Java HotSpot VM. default value for this option is `3`. See the `-XX:+UseStringDeduplication` option. -`-XX:SurvivorRatio=`*ratio* +[`-XX:SurvivorRatio=`]{#-XX_SurvivorRatio}*ratio* : Sets the ratio between eden space size and survivor space size. By default, this option is set to 8. The following example shows how to set the eden/survivor space ratio to 4: > `-XX:SurvivorRatio=4` -`-XX:TargetSurvivorRatio=`*percent* +[`-XX:TargetSurvivorRatio=`]{#-XX_TargetSurvivorRatio}*percent* : Sets the desired percentage of survivor space (0 to 100) used after young garbage collection. By default, this option is set to 50%. @@ -2757,7 +2757,7 @@ Java HotSpot VM. > `-XX:TargetSurvivorRatio=30` -`-XX:TLABSize=`*size* +[`-XX:TLABSize=`]{#-XX_TLABSize}*size* : Sets the initial size (in bytes) of a thread-local allocation buffer (TLAB). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. If this option is @@ -2767,13 +2767,13 @@ Java HotSpot VM. > `-XX:TLABSize=512k` -`-XX:+UseAdaptiveSizePolicy` +[`-XX:+UseAdaptiveSizePolicy`]{#-XX__UseAdaptiveSizePolicy} : Enables the use of adaptive generation sizing. This option is enabled by default. To disable adaptive generation sizing, specify `-XX:-UseAdaptiveSizePolicy` and set the size of the memory allocation pool explicitly. See the `-XX:SurvivorRatio` option. -`-XX:+UseG1GC` +[`-XX:+UseG1GC`]{#-XX__UseG1GC} : Enables the use of the garbage-first (G1) garbage collector. It's a server-style garbage collector, targeted for multiprocessor machines with a large amount of RAM. This option meets GC pause time goals with high @@ -2783,7 +2783,7 @@ Java HotSpot VM. pause time below 0.5 seconds). By default, this option is enabled and G1 is used as the default garbage collector. -`-XX:+UseGCOverheadLimit` +[`-XX:+UseGCOverheadLimit`]{#-XX__UseGCOverheadLimit} : Enables the use of a policy that limits the proportion of time spent by the JVM on GC before an `OutOfMemoryError` exception is thrown. This option is enabled, by default, and the parallel GC will throw an `OutOfMemoryError` @@ -2793,26 +2793,26 @@ Java HotSpot VM. little or no progress. To disable this option, specify the option `-XX:-UseGCOverheadLimit`. -`-XX:+UseNUMA` +[`-XX:+UseNUMA`]{#-XX__UseNUMA} : Enables performance optimization of an application on a machine with nonuniform memory architecture (NUMA) by increasing the application's use of lower latency memory. The default value for this option depends on the garbage collector. -`-XX:+UseParallelGC` +[`-XX:+UseParallelGC`]{#-XX__UseParallelGC} : Enables the use of the parallel scavenge garbage collector (also known as the throughput collector) to improve the performance of your application by leveraging multiple processors. By default, this option is disabled and the default collector is used. -`-XX:+UseSerialGC` +[`-XX:+UseSerialGC`]{#-XX__UseSerialGC} : Enables the use of the serial garbage collector. This is generally the best choice for small and simple applications that don't require any special functionality from garbage collection. By default, this option is disabled and the default collector is used. -`-XX:+UseStringDeduplication` +[`-XX:+UseStringDeduplication`]{#-XX__UseStringDeduplication} : Enables string deduplication. By default, this option is disabled. To use this option, you must enable the garbage-first (G1) garbage collector. @@ -2822,34 +2822,34 @@ Java HotSpot VM. character array, identical `String` objects can point to and share the same character array. -`-XX:+UseTLAB` +[`-XX:+UseTLAB`]{#-XX__UseTLAB} : Enables the use of thread-local allocation blocks (TLABs) in the young generation space. This option is enabled by default. To disable the use of TLABs, specify the option `-XX:-UseTLAB`. -`-XX:+UseZGC` +[`-XX:+UseZGC`]{#-XX__UseZGC} : Enables the use of the Z garbage collector (ZGC). This is a low latency garbage collector, providing max pause times of a few milliseconds, at some throughput cost. Pause times are independent of what heap size is used. Supports heap sizes from 8MB to 16TB. -`-XX:ZAllocationSpikeTolerance=`*factor* +[`-XX:ZAllocationSpikeTolerance=`]{#-XX_ZAllocationSpikeTolerance}*factor* : Sets the allocation spike tolerance for ZGC. By default, this option is set to 2.0. This factor describes the level of allocation spikes to expect. For example, using a factor of 3.0 means the current allocation rate can be expected to triple at any time. -`-XX:ZCollectionInterval=`*seconds* +[`-XX:ZCollectionInterval=`]{#-XX_ZCollectionInterval}*seconds* : Sets the maximum interval (in seconds) between two GC cycles when using ZGC. By default, this option is set to 0 (disabled). -`-XX:ZFragmentationLimit=`*percent* +[`-XX:ZFragmentationLimit=`]{#-XX_ZFragmentationLimit}*percent* : Sets the maximum acceptable heap fragmentation (in percent) for ZGC. By default, this option is set to 25. Using a lower value will cause the heap to be compacted more aggressively, to reclaim more memory at the cost of using more CPU time. -`-XX:+ZProactive` +[`-XX:+ZProactive`]{#-XX__ZProactive} : Enables proactive GC cycles when using ZGC. By default, this option is enabled. ZGC will start a proactive GC cycle if doing so is expected to have minimal impact on the running application. This is useful if the @@ -2857,27 +2857,27 @@ Java HotSpot VM. want to keep the heap size down and allow reference processing to happen even when there are a lot of free space on the heap. -`-XX:+ZUncommit` +[`-XX:+ZUncommit`]{#-XX__ZUncommit} : Enables uncommitting of unused heap memory when using ZGC. By default, this option is enabled. Uncommitting unused heap memory will lower the memory footprint of the JVM, and make that memory available for other processes to use. -`-XX:ZUncommitDelay=`*seconds* +[`-XX:ZUncommitDelay=`]{#-XX_ZUncommitDelay}*seconds* : Sets the amount of time (in seconds) that heap memory must have been unused before being uncommitted. By default, this option is set to 300 (5 minutes). Committing and uncommitting memory are relatively expensive operations. Using a lower value will cause heap memory to be uncommitted earlier, at the risk of soon having to commit it again. -`-XX:+UseShenandoahGC` +[`-XX:+UseShenandoahGC`]{#-XX__UseShenandoahGC} : Enables the use of the Shenandoah garbage collector. This is a low pause time, concurrent garbage collector. Its pause times are not proportional to the size of the heap. Shenandoah garbage collector can work with compressed pointers. See `-XX:UseCompressedOops` for further information about compressed pointers. -`-XX:ShenandoahGCMode=`*mode* +[`-XX:ShenandoahGCMode=`]{#-XX_ShenandoahGCMode}*mode* : Sets the GC mode for Shenandoah GC to use. By default, this option is set to `satb`. Among other things, this defines which barriers are in use. Possible mode values include the following: @@ -2891,7 +2891,7 @@ Java HotSpot VM. generational. Please see [JEP 404](https://openjdk.org/jeps/404) and [JEP 521](https://openjdk.org/jeps/521) for its advantages and risks. -`-XX:ShenandoahGCHeuristics=`*heuristics* +[`-XX:ShenandoahGCHeuristics=`]{#-XX_ShenandoahGCHeuristics}*heuristics* : Sets the heuristics for Shenandoah GC to use. By default, this option is set to `adaptive`. This fine-tunes the GC mode selected, by choosing when to start the GC, how much to process on each cycle, and what other features @@ -2916,7 +2916,7 @@ These `java` options are deprecated and might be removed in a future JDK release. They're still accepted and acted upon, but a warning is issued when they're used. -`-Xloggc:`*filename* +[`-Xloggc:`]{#-Xloggc_}*filename* : Sets the file to which verbose GC events information should be redirected for logging. The `-Xloggc` option overrides `-verbose:gc` if both are given with the same java command. `-Xloggc:`*filename* is replaced by @@ -2927,11 +2927,11 @@ they're used. `-Xlog:gc:garbage-collection.log` -`-XX:+FlightRecorder` +[`-XX:+FlightRecorder`]{#-XX__FlightRecorder} : Enables the use of Java Flight Recorder (JFR) during the runtime of the application. Since JDK 8u40 this option has not been required to use JFR. -`-XX:+ParallelRefProcEnabled` +[`-XX:+ParallelRefProcEnabled`]{#-XX__ParallelRefProcEnabled} : Enables parallel reference processing. By default, collectors employing multiple threads perform parallel reference processing if the number of parallel threads to use is larger than one. @@ -2939,7 +2939,7 @@ they're used. (`-XX:+UseParallelGC` or `-XX:+UseG1GC`). Other collectors employing multiple threads always perform reference processing in parallel. -`-XX:MaxRAM=`*size* +[`-XX:MaxRAM=`]{#-XX_MaxRAM}*size* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics. The default value is the amount of available memory to the JVM process. @@ -2958,13 +2958,13 @@ they're used. > `-XX:MaxRAM=2G` -`-XX:+AggressiveHeap` +[`-XX:+AggressiveHeap`]{#-XX__AggressiveHeap} : Enables Java heap optimization. This sets various parameters to be optimal for long-running jobs with intensive memory allocation, based on the configuration of the computer (RAM and CPU). By default, the option is disabled and the heap sizes are configured less aggressively. -`-XX:+NeverActAsServerClassMachine` +[`-XX:+NeverActAsServerClassMachine`]{#-XX__NeverActAsServerClassMachine} : Enable the "Client VM emulation" mode which only uses the C1 JIT compiler, a 32Mb CodeCache and the Serial GC. The maximum amount of memory that the JVM may use (controlled by the `-XX:MaxRAM=n` flag) is set to 1GB by default. @@ -2989,7 +2989,7 @@ they're used. These `java` options are still accepted but ignored, and a warning is issued when they're used. -`--illegal-access=`*parameter* +[`--illegal-access=`]{#--illegal-access}*parameter* : Controlled _relaxed strong encapsulation_, as defined in [JEP 261](https://openjdk.org/jeps/261#Relaxed-strong-encapsulation). This option was deprecated in JDK 16 by [JEP @@ -3283,7 +3283,7 @@ Flags to Xlog]. The following provides quick reference to the `-Xlog` command and syntax for options: -`-Xlog` +[`-Xlog`]{#-Xlog} : Enables JVM logging on an `info` level. `-Xlog:help` @@ -3570,7 +3570,7 @@ Legacy Runtime Flag Xlog Configuration Comment The following are `-Xlog` examples. -`-Xlog` +[`-Xlog`]{#-Xlog} : Logs all messages by using the `info` level to `stdout` with `uptime`, `levels`, and `tags` decorations. This is equivalent to using: @@ -4075,7 +4075,7 @@ The deployment of the AOT cache is divided into three phases: The AOT cache can be used with the following command-line options: -`-XX:AOTCache=`*cachefile* +[`-XX:AOTCache=`]{#-XX_AOTCache}*cachefile* : Specifies the location of the AOT cache. The standard extension for *cachefile* is `.aot`. This option cannot be used together with `-XX:AOTCacheOutput`. @@ -4084,13 +4084,13 @@ The AOT cache can be used with the following command-line options: The *cachefile* is written by AOT mode `create`. In that case, this option is equivalent to `-XX:AOTCacheOutput=`*cachefile*. -`-XX:AOTCacheOutput=`*cachefile* +[`-XX:AOTCacheOutput=`]{#-XX_AOTCacheOutput}*cachefile* : Specifies the location of the AOT cache to write. The standard extension for *cachefile* is `.aot`. This option cannot be used together with `-XX:AOTCache`. This option is compatible with `AOTMode` settings of `record`, `create`, or `auto` (the default). -`-XX:AOTConfiguration=`*configfile* +[`-XX:AOTConfiguration=`]{#-XX_AOTConfiguration}*configfile* : Specifies the AOT Configuration file for the JVM to write to or read from. The standard extension for *configfile* is `.aotconfig`. @@ -4098,7 +4098,7 @@ The AOT cache can be used with the following command-line options: The *configfile* is read by AOT mode `create`, and written by the other applicable modes. If the AOT mode is `auto`, then `AOTCacheOutput` must also be present. -`-XX:AOTMode=`*mode* +[`-XX:AOTMode=`]{#-XX_AOTMode}*mode* : Specifies the AOT Mode for this run. *mode* must be one of the following: `auto`, `off`, `record`, `create`, or `on`. @@ -4170,7 +4170,7 @@ The AOT cache can be used with the following command-line options: options are compatible with the AOT cache. An alternative is to run your application with `-XX:AOTMode=auto -Xlog:aot` to see if the AOT cache can be used or not. -`-XX:+AOTClassLinking` +[`-XX:+AOTClassLinking`]{#-XX__AOTClassLinking} : If this option is enabled, the JVM will perform more advanced optimizations (such as ahead-of-time resolution of invokedynamic instructions) when creating the AOT cache. As a result, the application will see further improvements diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c index 2e81cbd05c2..bcac334191c 100644 --- a/src/java.base/unix/native/libjava/io_util_md.c +++ b/src/java.base/unix/native/libjava/io_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "jvm.h" #include "io_util.h" #include "io_util_md.h" +#include #include #include @@ -75,20 +76,26 @@ FD handleOpen(const char *path, int oflag, int mode) { FD fd; RESTARTABLE(open(path, oflag, mode), fd); - if (fd != -1) { - struct stat buf; - int result; - RESTARTABLE(fstat(fd, &buf), result); - if (result != -1) { - if (S_ISDIR(buf.st_mode)) { - close(fd); - errno = EISDIR; - fd = -1; - } - } else { + // No further checking is needed if the file is not a + // directory or open returned an error + if (fd == -1 || ((oflag & O_ACCMODE) != O_RDONLY) != 0) { + return fd; + } + + // FileInputStream is specified to throw if the + // file is a directory + struct stat buf; + int result; + RESTARTABLE(fstat(fd, &buf), result); + if (result != -1) { + if (S_ISDIR(buf.st_mode)) { close(fd); + errno = EISDIR; fd = -1; } + } else { + close(fd); + fd = -1; } return fd; } diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java index 2d92504edb6..2ffed3f7942 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,8 @@ import javax.lang.model.type.DeclaredType; * method. There is no guarantee that any particular annotation will * always be represented by the same object. * + * @jls 9.6 Annotation Interfaces + * @jls 9.7 Annotations * @since 1.6 */ public interface AnnotationMirror { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java index 77521dac191..71594d31150 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,14 +27,16 @@ package javax.lang.model.element; /** * Represents a value of an annotation interface element. - * A value is of one of the following types: - *

  • a wrapper class (such as {@link Integer}) for a primitive type - *
  • {@code String} - *
  • {@code TypeMirror} - *
  • {@code VariableElement} (representing an enum constant) - *
  • {@code AnnotationMirror} + * A value is of one of the following types (JLS {@jls 9.6.1}): + *
    • a {@linkplain java.lang##wrapperClass wrapper class} to hold a + * primitive type, such as an {@code Integer} object to hold an + * {@code int} + *
    • {@code String} representing a {@code String} + *
    • {@link javax.lang.model.type.TypeMirror TypeMirror} representing a {@code Class} literal + *
    • {@link VariableElement} representing an enum constant + *
    • {@link AnnotationMirror} representing an annotation *
    • {@code List} - * (representing the elements, in declared order, if the value is an array) + * representing the elements, in declared order, if the value is an array *
    * * @since 1.6 diff --git a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java index 18923463248..cc0924fda61 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,13 @@ import javax.lang.model.type.*; * clause, among other restrictions; see JLS {@jls 9.6.1} for details. * * @see ExecutableType + * @jls 8.4 Method Declarations + * @jls 8.6 Instance Initializers + * @jls 8.7 Static Initializers + * @jls 8.8 Constructor Declarations + * @jls 9.4 Method Declarations + * @jls 9.6.1 Annotation Interface Elements + * * @since 1.6 */ public interface ExecutableElement extends Element, Parameterizable { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java b/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java index 651bbe70eee..79b1fb84683 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,8 @@ import javax.lang.model.type.TypeMirror; *
* * @see javax.lang.model.util.Elements#getPackageOf + * @jls 7.4 Package Declarations + * * @since 1.6 */ public interface PackageElement extends Element, QualifiedNameable { @@ -87,6 +89,7 @@ public interface PackageElement extends Element, QualifiedNameable { * @return the fully qualified name of this package, or an * empty name if this is an unnamed package * @jls 6.7 Fully Qualified Names and Canonical Names + * @jls 7.4.1 Named Packages */ Name getQualifiedName(); diff --git a/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java b/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java index cf0bbdfba37..a4280fbcf29 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, 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 @@ -30,6 +30,10 @@ import java.util.List; /** * A mixin interface for an element that has type parameters. * + * @jls 4.5 Parameterized Types + * @jls 8.4.4 Generic Methods + * @jls 8.8.4 Generic Constructors + * * @since 1.7 */ public interface Parameterizable extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java b/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java index 1b0316f893f..555d90c1b13 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, 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 @@ -28,6 +28,10 @@ package javax.lang.model.element; /** * A mixin interface for an element that has a qualified name. * + * @jls 6.5.3.2 Qualified Package Names + * @jls 6.5.5.2 Qualified Type Names + * @jls 6.7 Fully Qualified Names and Canonical Names + * * @since 1.7 */ public interface QualifiedNameable extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java index abf95bcefad..3dcc8e8c7fd 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,7 +80,16 @@ import javax.lang.model.util.*; * javax.lang.model.util.Elements#getAllTypeElements(CharSequence) * queried for} in the configured environment * + * * @see DeclaredType + * @jls 8.1 Class Declarations + * @jls 8.5 Member Class and Interface Declarations + * @jls 8.9 Enum Classes + * @jls 8.10 Record Classes + * @jls 9.1 Interface Declarations + * @jls 9.5 Member Class and Interface Declarations + * @jls 9.6 Annotation Interfaces + * * @since 1.6 */ public interface TypeElement extends Element, Parameterizable, QualifiedNameable { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java index cbaa8969126..abd06a3e038 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,11 @@ import javax.lang.model.type.TypeVariable; * A type parameter declares a {@link TypeVariable}. * * @see TypeVariable + * @jls 8.1.2 Generic Classes and Type Parameters + * @jls 8.4.4 Generic Methods + * @jls 8.8.4 Generic Constructors + * @jls 9.1.2 Generic Interfaces and Type Parameters + * * @since 1.6 */ public interface TypeParameterElement extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java index 4d2b2582901..b8f42c46b1e 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,14 @@ import javax.lang.model.type.TypeKind; * parameter, local variable, resource variable, or exception * parameter. * + * @jls 8.3 Field Declaration + * @jls 8.9.1 Enum Constants + * @jls 8.4.1 Formal Parameters + * @jls 8.8.1 Formal Parameters + * @jls 14.4 Local Variable Declarations + * @jls 14.20 The {@code try} statement + * @jls 14.20.3 {@code try}-with-resources + * @since 1.6 */ public interface VariableElement extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java index 5bd205a6c4b..facdbe405dd 100644 --- a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -120,6 +120,15 @@ public interface TypeMirror extends AnnotatedConstruct { * The results of {@code t1.equals(t2)} and * {@code Types.isSameType(t1, t2)} may differ. * + * @apiNote The identity of a {@code TypeMirror} involves implicit + * state not directly accessible from its methods, including state + * about the presence of unrelated types. {@code TypeMirror} + * objects created by different implementations of these + * interfaces should not be expected to compare as equal + * even if "the same" type is being modeled; this is + * analogous to the inequality of {@code Class} objects for the + * same class file loaded through different class loaders. + * * @param obj the object to be compared with this type * @return {@code true} if the specified object is equal to this one */ diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Types.java b/src/java.compiler/share/classes/javax/lang/model/util/Types.java index 951b56ed214..e7212a7e0be 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/Types.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/Types.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,6 +106,15 @@ public interface Types { * {@code TypeMirror} objects can have different annotations and * still be considered the same. * + * @apiNote The identity of a {@code TypeMirror} involves implicit + * state not directly accessible from its methods, including state + * about the presence of unrelated types. {@code TypeMirror} + * objects created by different implementations of these + * interfaces should not be expected to compare as equal + * even if "the same" type is being modeled; this is + * analogous to the inequality of {@code Class} objects for the + * same class file loaded through different class loaders. + * * @param t1 the first type * @param t2 the second type * @return {@code true} if and only if the two types are the same diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java index da1785a9a63..5e68cc7a82c 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java @@ -35,8 +35,6 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.UIResource; -import sun.awt.AppContext; - import sun.lwawt.macosx.CPlatformWindow; import sun.swing.SwingUtilities2; @@ -150,13 +148,34 @@ final class AquaUtils { protected abstract T create(); } - abstract static class RecyclableSingleton { - final T get() { - return AppContext.getSoftReferenceValue(this, () -> getInstance()); - } + abstract static class LazySingleton { + T instance; - void reset() { - AppContext.getAppContext().remove(this); + final T get() { + if (instance == null) { + instance = getInstance(); + } + return instance; + } + + abstract T getInstance(); + } + + abstract static class RecyclableSingleton { + + SoftReference ref; + + final T get() { + T instance; + if (ref != null) { + instance = ref.get(); + if (instance != null) { + return instance; + } + } + instance = getInstance(); + ref = new SoftReference<>(instance); + return instance; } abstract T getInstance(); @@ -197,11 +216,11 @@ final class AquaUtils { protected abstract V getInstance(K key); } - private static final RecyclableSingleton enableAnimations = new RecyclableSingleton() { + private static final LazySingleton enableAnimations = new LazySingleton() { @Override protected Boolean getInstance() { - final String sizeProperty = System.getProperty(ANIMATIONS_PROPERTY); - return !"false".equals(sizeProperty); // should be true by default + final String animationsProperty = System.getProperty(ANIMATIONS_PROPERTY); + return !"false".equals(animationsProperty); // should be true by default } }; private static boolean animationsEnabled() { diff --git a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp index 441a71f5c50..bae16cb0a9c 100644 --- a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp +++ b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -162,8 +162,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre sampleRate, // sample rate DAUDIO_PCM, // only accept PCM bits == 8 ? FALSE : TRUE, // signed - bits == 8 ? FALSE // little-endian for 8bit - : UTIL_IsBigEndianPlatform()); + FALSE); // all supported macOS versions run on LE } } // add default format @@ -175,7 +174,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre defSampleRate, // sample rate DAUDIO_PCM, // PCM TRUE, // signed - UTIL_IsBigEndianPlatform()); // native endianness + FALSE); // native endianness; all supported macOS versions run on LE } TRACE0("< T getSoftReferenceValue(Object key, - Supplier supplier) { - - final AppContext appContext = AppContext.getAppContext(); - @SuppressWarnings("unchecked") - SoftReference ref = (SoftReference) appContext.get(key); - if (ref != null) { - final T object = ref.get(); - if (object != null) { - return object; - } - } - final T object = supplier.get(); - ref = new SoftReference<>(object); - appContext.put(key, ref); - return object; - } } final class MostRecentKeyValue { diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageCache.java b/src/java.desktop/share/classes/sun/awt/image/ImageCache.java index eed1aa5bdb7..b7ac69923da 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageCache.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageCache.java @@ -29,7 +29,6 @@ import java.awt.*; import java.lang.ref.*; import java.util.*; import java.util.concurrent.locks.*; -import sun.awt.AppContext; /** * ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary @@ -37,8 +36,6 @@ import sun.awt.AppContext; * dropped by the GC if heap memory gets tight. When our size hits max pixel * count least recently requested images are removed first. * - * The ImageCache must be used from the thread with an AppContext only. - * */ public final class ImageCache { @@ -56,9 +53,10 @@ public final class ImageCache { // Reference queue for tracking lost softreferences to images in the cache private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private static final ImageCache instance = new ImageCache(); + public static ImageCache getInstance() { - return AppContext.getSoftReferenceValue(ImageCache.class, - () -> new ImageCache()); + return instance; } ImageCache(final int maxPixelCount) { diff --git a/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java index 357ad3be43f..08909b802c5 100644 --- a/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -302,8 +302,16 @@ public class PNGImageDecoder extends ImageDecoder int bitsPerPixel = samplesPerPixel*bitDepth; int bytesPerPixel = (bitsPerPixel+7)>>3; int pass, passLimit; - if(interlaceMethod==0) { pass = -1; passLimit = 0; } - else { pass = 0; passLimit = 7; } + boolean isDirectByteCopy; + if(interlaceMethod==0) { + pass = -1; + passLimit = 0; + isDirectByteCopy = bPixels != null && bitDepth == 8; + } else { + pass = 0; + passLimit = 7; + isDirectByteCopy = false; + } while(++pass<=passLimit) { int row = startingRow[pass]; int rowInc = rowIncrement[pass]; @@ -334,7 +342,11 @@ public class PNGImageDecoder extends ImageDecoder int spos=0; int pixel = 0; while (col < width) { - if(wPixels !=null) { + if (isDirectByteCopy) { + System.arraycopy(rowByteBuffer, spos, bPixels, col + rowOffset, width); + spos += width; + break; + } else if(wPixels !=null) { switch(combinedType) { case COLOR|ALPHA|(8<<3): wPixels[col+rowOffset] = diff --git a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java index cac9f8baab2..bc025b87f3c 100644 --- a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -92,8 +92,8 @@ public class XbmImageDecoder extends ImageDecoder { byte[] raster = null; IndexColorModel model = null; - String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]"; - String replaceRegex = "(0[xX])|,|[\\s+]|[};]"; + String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)"; + String replaceRegex = "0[xX]|,|\\s+|\\};"; String line; int lineNum = 0; @@ -111,11 +111,19 @@ public class XbmImageDecoder extends ImageDecoder { } try { if (!token[2].isBlank() && state == 0) { - W = Integer.parseInt(token[2]); - state = 1; // after width is set + if (token[1].endsWith("th")) { + W = Integer.parseInt(token[2]); + } else if (token[1].endsWith("t")) { + H = Integer.parseInt(token[2]); + } + state = 1; // after first dimension is set } else if (!token[2].isBlank() && state == 1) { - H = Integer.parseInt(token[2]); - state = 2; // after height is set + if (token[1].endsWith("th")) { + W = Integer.parseInt(token[2]); + } else if (token[1].endsWith("t")) { + H = Integer.parseInt(token[2]); + } + state = 2; // after second dimension is set } } catch (NumberFormatException nfe) { // parseInt() can throw NFE @@ -147,59 +155,81 @@ public class XbmImageDecoder extends ImageDecoder { error("Width or Height of XBM file not defined"); } + boolean contFlag = false; + StringBuilder sb = new StringBuilder(); + // loop to process image data while (!aborted && (line = br.readLine()) != null) { lineNum++; - if (line.contains("[]")) { - Matcher matcher = Pattern.compile(matchRegex).matcher(line); - while (matcher.find()) { - if (y >= H) { - error("Scan size of XBM file exceeds" - + " the defined width x height"); - } - - int startIndex = matcher.start(); - int endIndex = matcher.end(); - String hexByte = line.substring(startIndex, endIndex); - - if (!(hexByte.startsWith("0x") - || hexByte.startsWith("0X"))) { - error("Invalid hexadecimal number at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - hexByte = hexByte.replaceAll(replaceRegex, ""); - if (hexByte.length() != 2) { - error("Invalid hexadecimal number at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - - try { - n = Integer.parseInt(hexByte, 16); - } catch (NumberFormatException nfe) { - error("Error parsing hexadecimal at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - for (int mask = 1; mask <= 0x80; mask <<= 1) { - if (x < W) { - if ((n & mask) != 0) - raster[x] = 1; - else - raster[x] = 0; - } - x++; - } - - if (x >= W) { - int result = setPixels(0, y, W, 1, model, raster, 0, W); - if (result <= 0) { - error("Unexpected error occurred during setPixel()"); - } - x = 0; - y++; - } + if (!contFlag) { + if (line.contains("[]")) { + contFlag = true; + } else { + continue; } } + + int end = line.indexOf(';'); + if (end >= 0) { + sb.append(line, 0, end + 1); + break; + } else { + sb.append(line).append(System.lineSeparator()); + } + } + + String resultLine = sb.toString(); + int cutOffIndex = resultLine.indexOf('{'); + resultLine = resultLine.substring(cutOffIndex + 1); + + Matcher matcher = Pattern.compile(matchRegex).matcher(resultLine); + while (matcher.find()) { + if (y >= H) { + error("Scan size of XBM file exceeds" + + " the defined width x height"); + } + + int startIndex = matcher.start(); + int endIndex = matcher.end(); + String hexByte = resultLine.substring(startIndex, endIndex); + hexByte = hexByte.replaceAll("^\\s+", ""); + + if (!(hexByte.startsWith("0x") + || hexByte.startsWith("0X"))) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + hexByte = hexByte.replaceAll(replaceRegex, ""); + if (hexByte.length() != 2) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + + try { + n = Integer.parseInt(hexByte, 16); + } catch (NumberFormatException nfe) { + error("Error parsing hexadecimal at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + for (int mask = 1; mask <= 0x80; mask <<= 1) { + if (x < W) { + if ((n & mask) != 0) + raster[x] = 1; + else + raster[x] = 0; + } + x++; + } + + if (x >= W) { + int result = setPixels(0, y, W, 1, model, raster, 0, W); + if (result <= 0) { + error("Unexpected error occurred during setPixel()"); + } + x = 0; + y++; + } } imageComplete(ImageConsumer.STATICIMAGEDONE, true); } diff --git a/src/java.desktop/share/native/libjsound/Utilities.c b/src/java.desktop/share/native/libjsound/Utilities.c index 6e92813d53c..50ca18c0259 100644 --- a/src/java.desktop/share/native/libjsound/Utilities.c +++ b/src/java.desktop/share/native/libjsound/Utilities.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2007, 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 @@ -26,15 +26,6 @@ #include "Utilities.h" - -int UTIL_IsBigEndianPlatform() { -#ifdef _LITTLE_ENDIAN - return 0; -#else - return 1; -#endif -} - void ThrowJavaMessageException(JNIEnv *e, const char *exClass, const char *msg) { jclass newExcCls; diff --git a/src/java.desktop/share/native/libjsound/Utilities.h b/src/java.desktop/share/native/libjsound/Utilities.h index fbecab1e005..7cb0dcbdd4c 100644 --- a/src/java.desktop/share/native/libjsound/Utilities.h +++ b/src/java.desktop/share/native/libjsound/Utilities.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, 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 @@ -28,10 +28,6 @@ #include "SoundDefs.h" #include "Configure.h" // put flags for debug msgs etc. here -// return 1 if this platform is big endian, or 0 for little endian -int UTIL_IsBigEndianPlatform(); - - // ERROR PRINTS #ifdef USE_ERROR #define ERROR0(string) { fprintf(stdout, (string)); fflush(stdout); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 3a8b4e5dbea..773c573c201 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -37,11 +37,7 @@ import java.util.Set; import java.util.stream.Stream; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; -import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -178,6 +174,14 @@ public class Lint { /** * Categories of warnings that can be generated by the compiler. + * Each lint category has a logical name (a string), which is the string used e.g. in a {@code SuppressWarning} annotation. + * To ensure automation, the enum field name for a lint category string {@code C} should be obtained by: + *
    + *
  1. capitalize all the letters in {@code C}, and
  2. + *
  3. replacing any occurrence of {@code -} with {@code _}
  4. + *
+ * For instance, the lint category string {@code dangling-doc-comments} corresponds to the enum field + * {@code DANGLING_DOC_COMMENTS}. */ public enum LintCategory { /** @@ -320,7 +324,7 @@ public class Lint { /** * Warn about unchecked operations on raw types. */ - RAW("rawtypes"), + RAWTYPES("rawtypes"), /** * Warn about use of deprecated-for-removal items. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index 6aae8eb855d..86319f20c73 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1403,6 +1403,7 @@ public class TypeAnnotations { break; } } + scan(tree.dims); scan(tree.elems); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index f5fdc1578b8..f865afe11fb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.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 @@ -1107,6 +1107,7 @@ public class Annotate { for (List dimAnnos : tree.dimAnnotations) enterTypeAnnotations(dimAnnos, env, sym, false); scan(tree.elemtype); + scan(tree.dims); scan(tree.elems); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index 08406fef518..02e15d5f8dd 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -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 @@ -2873,9 +2873,9 @@ public abstract class ByteVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (byte) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (byte) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (byte) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (byte) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (byte) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((byte)0, m, (i, a, b) -> (byte) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2890,6 +2890,8 @@ public abstract class ByteVector extends AbstractVector { private static final byte MIN_OR_INF = Byte.MIN_VALUE; private static final byte MAX_OR_INF = Byte.MAX_VALUE; + private static final byte UMIN_VALUE = (byte)0; // Minimum unsigned value + private static final byte UMAX_VALUE = (byte)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 786cd089ebe..08fda9c96e6 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -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 diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index b481d5a51d7..0f70a2b81c8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -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 diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 43356b9ea6c..23e703dcada 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -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 @@ -2858,9 +2858,9 @@ public abstract class IntVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (int) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (int) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (int) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (int) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (int) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((int)0, m, (i, a, b) -> (int) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2875,6 +2875,8 @@ public abstract class IntVector extends AbstractVector { private static final int MIN_OR_INF = Integer.MIN_VALUE; private static final int MAX_OR_INF = Integer.MAX_VALUE; + private static final int UMIN_VALUE = (int)0; // Minimum unsigned value + private static final int UMAX_VALUE = (int)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 8947343ff30..58bfd4d7772 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -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 @@ -2724,9 +2724,9 @@ public abstract class LongVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (long) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (long) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (long) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (long) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (long) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((long)0, m, (i, a, b) -> (long) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2741,6 +2741,8 @@ public abstract class LongVector extends AbstractVector { private static final long MIN_OR_INF = Long.MIN_VALUE; private static final long MAX_OR_INF = Long.MAX_VALUE; + private static final long UMIN_VALUE = (long)0; // Minimum unsigned value + private static final long UMAX_VALUE = (long)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, 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 e222c6d25f3..7ab7e7c4417 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 @@ -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 @@ -2874,9 +2874,9 @@ public abstract class ShortVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (short) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (short) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (short) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (short) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (short) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((short)0, m, (i, a, b) -> (short) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2891,6 +2891,8 @@ public abstract class ShortVector extends AbstractVector { private static final short MIN_OR_INF = Short.MIN_VALUE; private static final short MAX_OR_INF = Short.MAX_VALUE; + private static final short UMIN_VALUE = (short)0; // Minimum unsigned value + private static final short UMAX_VALUE = (short)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, 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 f7d987fd280..ce3b7512c93 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 @@ -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 @@ -3448,9 +3448,9 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($type$) Math.max(a, b))); #if[!FP] case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> ($type$) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> ($type$) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($type$) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> ($type$) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp(($type$)0, m, (i, a, b) -> ($type$) VectorMath.addSaturatingUnsigned(a, b))); #end[!FP] @@ -3472,6 +3472,8 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { #else[FP] private static final $type$ MIN_OR_INF = $Boxtype$.MIN_VALUE; private static final $type$ MAX_OR_INF = $Boxtype$.MAX_VALUE; + private static final $type$ UMIN_VALUE = ($type$)0; // Minimum unsigned value + private static final $type$ UMAX_VALUE = ($type$)-1; // Maximum unsigned value #end[FP] public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java index dbafca4ed3c..e06d361b203 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java @@ -58,7 +58,13 @@ final class Instrumentation { } public void addMethod(long methodId, String name, String signature, int modification) { - modificationMap.put(name + signature, new Method(methodId, Modification.valueOf(modification), className + "::" + name)); + Method method = new Method( + methodId, + Modification.valueOf(modification), + name.equals(""), + className + "::" + name + ); + modificationMap.put(name + signature, method); } public List getMethods() { @@ -71,7 +77,7 @@ final class Instrumentation { ClassModel classModel = classFile.parse(bytecode); byte[] generated = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> { for (var ce : classModel) { - if (modifyClassElement(classBuilder, ce)) { + if (modifyClassElement(classModel, classBuilder, ce)) { modified[0] = true; } else { classBuilder.with(ce); @@ -93,7 +99,7 @@ final class Instrumentation { } } - private boolean modifyClassElement(ClassBuilder classBuilder, ClassElement ce) { + private boolean modifyClassElement(ClassModel classModel, ClassBuilder classBuilder, ClassElement ce) { if (ce instanceof MethodModel mm) { String method = mm.methodName().stringValue(); String signature = mm.methodType().stringValue(); @@ -102,15 +108,15 @@ final class Instrumentation { if (tm != null) { Modification m = tm.modification(); if (m.tracing() || m.timing()) { - return modifyMethod(classBuilder, mm, tm); + return modifyMethod(classModel, classBuilder, mm, tm); } } } return false; } - private boolean modifyMethod(ClassBuilder classBuilder, MethodModel m, Method method) { - var code = m.code(); + private boolean modifyMethod(ClassModel classModel, ClassBuilder classBuilder, MethodModel methodModel, Method method) { + var code = methodModel.code(); if (code.isPresent()) { if (classLoader == null && ExcludeList.containsMethod(method.name())) { String msg = "Risk of recursion, skipping bytecode generation of " + method.name(); @@ -118,9 +124,9 @@ final class Instrumentation { return false; } MethodTransform s = MethodTransform.ofStateful( - () -> MethodTransform.transformingCode(new Transform(method)) + () -> MethodTransform.transformingCode(new Transform(classModel, code.get(), method)) ); - classBuilder.transformMethod(m, s); + classBuilder.transformMethod(methodModel, s); return true; } return false; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java index d685083153d..d85e458e9d5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java @@ -31,7 +31,7 @@ import jdk.jfr.internal.Logger; /** * Class that holds information about an instrumented method. */ -record Method(long methodId, Modification modification, String name) { +record Method(long methodId, Modification modification, boolean constructor, String name) { @Override public String toString() { return name + (modification.timing() ? " +timing" : " -timing") + (modification.tracing() ? " +tracing" : " -tracing") + " (Method ID: " + String.format("0x%08X)", methodId); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java index cd65a119cee..377eede7925 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java @@ -24,13 +24,19 @@ */ package jdk.jfr.internal.tracing; +import java.lang.classfile.ClassModel; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeModel; import java.lang.classfile.CodeTransform; +import java.lang.classfile.Label; import java.lang.classfile.TypeKind; +import java.lang.classfile.instruction.InvokeInstruction; import java.lang.classfile.instruction.ReturnInstruction; import java.lang.classfile.instruction.ThrowInstruction; import java.lang.constant.ClassDesc; +import java.util.ArrayList; +import java.util.List; import jdk.jfr.internal.util.Bytecode; import jdk.jfr.internal.util.Bytecode.MethodDesc; @@ -43,59 +49,208 @@ import jdk.jfr.tracing.MethodTracer; * The method ID is determined by native code. */ final class Transform implements CodeTransform { + private static class TryBlock { + Label start; + Label end; + } private static final ClassDesc METHOD_TRACER_CLASS = ClassDesc.of(MethodTracer.class.getName()); private static final MethodDesc TRACE_METHOD = MethodDesc.of("trace", "(JJ)V"); private static final MethodDesc TIMING_METHOD = MethodDesc.of("timing", "(JJ)V"); private static final MethodDesc TRACE_TIMING_METHOD = MethodDesc.of("traceTiming", "(JJ)V"); private static final MethodDesc TIMESTAMP_METHOD = MethodDesc.of("timestamp", "()J"); + private final List tryBlocks = new ArrayList<>(); + private final boolean simplifiedInstrumentation; + private final ClassModel classModel; private final Method method; private int timestampSlot = -1; - Transform(Method method) { + Transform(ClassModel classModel, CodeModel model, Method method) { this.method = method; + this.classModel = classModel; + // The JVMS (not the JLS) allows multiple mutually exclusive super/this. + // invocations in a constructor body as long as only one lies on any given + // execution path. For example, this is valid bytecode: + // + // Foo(boolean value) { + // if (value) { + // staticMethodThatMayThrow(); + // super(); + // } else { + // try { + // if (value == 0) { + // throw new Exception(""); + // } + // } catch (Throwable t) { + // throw t; + // } + // super(); + // } + // } + // + // If such a method is found, instrumentation falls back to instrumenting only + // RET and ATHROW. This can cause exceptions to be missed or counted twice. + // + // An effect of this heuristic is that constructors like the one below + // will also trigger simplified instrumentation. + // + // class Bar { + // } + // + // class Foo extends Bar { + // Foo() { + // new Bar(); + // } + // } + // + // java.lang.Object:: with zero constructor invocations should use simplified instrumentation + this.simplifiedInstrumentation = method.constructor() && constructorInvocations(model.elementList()) != 1; + } + + private int constructorInvocations(List elementList) { + int count = 0; + for (CodeElement e : elementList) { + if (isConstructorInvocation(e)) { + count++; + } + } + return count; + } + + private boolean isConstructorInvocation(CodeElement element) { + if (element instanceof InvokeInstruction inv && inv.name().equalsString("")) { + if (classModel.thisClass().equals(inv.owner())) { + return true; + } + if (classModel.superclass().isPresent()) { + return classModel.superclass().get().equals(inv.owner()); + } + } + return false; } @Override - public final void accept(CodeBuilder builder, CodeElement element) { + public void accept(CodeBuilder builder, CodeElement element) { + if (simplifiedInstrumentation) { + acceptSimplifiedInstrumentation(builder, element); + return; + } + if (method.constructor()) { + acceptConstructor(builder, element, isConstructorInvocation(element)); + } else { + acceptMethod(builder, element); + } + } + + @Override + public void atEnd(CodeBuilder builder) { + endTryBlock(builder); + for (TryBlock block : tryBlocks) { + addCatchHandler(block, builder); + } + } + + private void acceptConstructor(CodeBuilder builder, CodeElement element, boolean isConstructorInvocation) { + if (timestampSlot == -1) { + timestampSlot = invokeTimestamp(builder); + builder.lstore(timestampSlot); + if (!isConstructorInvocation) { + beginTryBlock(builder); + } + } + if (isConstructorInvocation) { + endTryBlock(builder); + builder.with(element); + beginTryBlock(builder); + return; + } + if (element instanceof ReturnInstruction) { + addTracing(builder); + } + builder.with(element); + } + + private void endTryBlock(CodeBuilder builder) { + if (tryBlocks.isEmpty()) { + return; + } + TryBlock last = tryBlocks.getLast(); + if (last.end == null) { + last.end = builder.newBoundLabel(); + } + } + + private void beginTryBlock(CodeBuilder builder) { + TryBlock block = new TryBlock(); + block.start = builder.newBoundLabel(); + tryBlocks.add(block); + } + + private void acceptSimplifiedInstrumentation(CodeBuilder builder, CodeElement element) { if (timestampSlot == -1) { timestampSlot = invokeTimestamp(builder); builder.lstore(timestampSlot); } if (element instanceof ReturnInstruction || element instanceof ThrowInstruction) { - builder.lload(timestampSlot); - builder.ldc(method.methodId()); - Modification modification = method.modification(); - boolean objectInit = method.name().equals("java.lang.Object::"); - String suffix = objectInit ? "ObjectInit" : ""; - if (modification.timing()) { - if (modification.tracing()) { - invokeTraceTiming(builder, suffix); - } else { - invokeTiming(builder, suffix); - } - } else { - if (modification.tracing()) { - invokeTrace(builder, suffix); - } - } + addTracing(builder); } builder.with(element); } - public static void invokeTiming(CodeBuilder builder, String suffix) { + private void acceptMethod(CodeBuilder builder, CodeElement element) { + if (timestampSlot == -1) { + timestampSlot = invokeTimestamp(builder); + builder.lstore(timestampSlot); + beginTryBlock(builder); + } + if (element instanceof ReturnInstruction) { + addTracing(builder); + } + builder.with(element); + } + + private void addCatchHandler(TryBlock block, CodeBuilder builder) { + Label catchHandler = builder.newBoundLabel(); + int exceptionSlot = builder.allocateLocal(TypeKind.REFERENCE); + builder.astore(exceptionSlot); + addTracing(builder); + builder.aload(exceptionSlot); + builder.athrow(); + builder.exceptionCatchAll(block.start, block.end, catchHandler); + } + + private void addTracing(CodeBuilder builder) { + builder.lload(timestampSlot); + builder.ldc(method.methodId()); + Modification modification = method.modification(); + boolean objectInit = method.name().equals("java.lang.Object::"); + String suffix = objectInit ? "ObjectInit" : ""; + if (modification.timing()) { + if (modification.tracing()) { + invokeTraceTiming(builder, suffix); + } else { + invokeTiming(builder, suffix); + } + } else { + if (modification.tracing()) { + invokeTrace(builder, suffix); + } + } + } + + private static void invokeTiming(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TIMING_METHOD.name() + suffix, TIMING_METHOD.descriptor()); } - public static void invokeTrace(CodeBuilder builder, String suffix) { + private static void invokeTrace(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TRACE_METHOD.name() + suffix, TRACE_METHOD.descriptor()); } - public static void invokeTraceTiming(CodeBuilder builder, String suffix) { + private static void invokeTraceTiming(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TRACE_TIMING_METHOD.name() + suffix, TRACE_TIMING_METHOD.descriptor()); } - public static int invokeTimestamp(CodeBuilder builder) { + private static int invokeTimestamp(CodeBuilder builder) { Bytecode.invokestatic(builder, METHOD_TRACER_CLASS, TIMESTAMP_METHOD); return builder.allocateLocal(TypeKind.LONG); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index dbaa5e3eec6..523b6c4821c 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ import jdk.jpackage.internal.model.LinuxLauncher; import jdk.jpackage.internal.model.LinuxPackage; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.util.CompositeProxy; +import jdk.jpackage.internal.util.Enquoter; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.XmlUtils; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java index 55200b908cd..6faacbca528 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, 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,13 +27,12 @@ package jdk.jpackage.internal; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Collection; -import java.util.Objects; import java.util.Collections; -import java.util.Set; -import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -48,9 +47,6 @@ public final class LibProvidersLookup { return (new ToolValidator(TOOL_LDD).validate() == null); } - public LibProvidersLookup() { - } - LibProvidersLookup setPackageLookup(PackageLookup v) { packageLookup = v; return this; @@ -87,23 +83,20 @@ public final class LibProvidersLookup { } private static List getNeededLibsForFile(Path path) throws IOException { - List result = new ArrayList<>(); - int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> { - lines.map(line -> { - Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line); - if (matcher.find()) { - return matcher.group(1); - } - return null; - }).filter(Objects::nonNull).map(Path::of).forEach(result::add); - }).execute(); + final var result = Executor.of(TOOL_LDD, path.toString()).saveOutput().execute(); - if (ret != 0) { + if (result.getExitCode() != 0) { // objdump failed. This is OK if the tool was applied to not a binary file return Collections.emptyList(); } - return result; + return result.stdout().stream().map(line -> { + Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line); + if (matcher.find()) { + return matcher.group(1); + } + return null; + }).filter(Objects::nonNull).map(Path::of).toList(); } private static Collection getNeededLibsForFiles(List paths) { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java index 6e438e66a26..d2169ede461 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.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,6 +33,7 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_R import java.util.Map; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Stream; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardBundlingOperation; @@ -44,19 +45,34 @@ import jdk.jpackage.internal.util.Result; public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { public LinuxBundlingEnvironment() { - super(build() - .defaultOperation(() -> { - return LazyLoad.SYS_ENV.value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get); - }) - .bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage) - .bundler(CREATE_LINUX_DEB, LazyLoad::debSysEnv, LinuxBundlingEnvironment::createDebPackage) - .bundler(CREATE_LINUX_RPM, LazyLoad::rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage)); + super(build().mutate(builder -> { + + // Wrap the generic Linux system environment supplier in the run-once wrapper + // as this supplier is called from both RPM and DEB Linux system environment suppliers. + var sysEnv = runOnce(() -> { + return LinuxSystemEnvironment.create(); + }); + + Supplier> debSysEnv = () -> { + return LinuxDebSystemEnvironment.create(sysEnv.get()); + }; + + Supplier> rpmSysEnv = () -> { + return LinuxRpmSystemEnvironment.create(sysEnv.get()); + }; + + builder.defaultOperation(() -> { + return sysEnv.get().value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get); + }) + .bundler(CREATE_LINUX_DEB, debSysEnv, LinuxBundlingEnvironment::createDebPackage) + .bundler(CREATE_LINUX_RPM, rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage); + }).bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage)); } private static void createDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) { createNativePackage(options, - LinuxFromOptions.createLinuxDebPackage(options), + LinuxFromOptions.createLinuxDebPackage(options, sysEnv), buildEnv()::create, LinuxBundlingEnvironment::buildPipeline, (env, pkg, outputDir) -> { @@ -67,7 +83,7 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { private static void createRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) { createNativePackage(options, - LinuxFromOptions.createLinuxRpmPackage(options), + LinuxFromOptions.createLinuxRpmPackage(options, sysEnv), buildEnv()::create, LinuxBundlingEnvironment::buildPipeline, (env, pkg, outputDir) -> { @@ -90,23 +106,6 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { return new BuildEnvFromOptions().predefinedAppImageLayout(APPLICATION_LAYOUT); } - private static final class LazyLoad { - - static Result debSysEnv() { - return DEB_SYS_ENV; - } - - static Result rpmSysEnv() { - return RPM_SYS_ENV; - } - - private static final Result SYS_ENV = LinuxSystemEnvironment.create(); - - private static final Result DEB_SYS_ENV = LinuxDebSystemEnvironment.create(SYS_ENV); - - private static final Result RPM_SYS_ENV = LinuxRpmSystemEnvironment.create(SYS_ENV); - } - private static final Map DESCRIPTORS = Stream.of( CREATE_LINUX_DEB, CREATE_LINUX_RPM diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java index 64a0368e9a0..7c1f06f54a3 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.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 @@ -25,7 +25,6 @@ package jdk.jpackage.internal; -import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; @@ -76,11 +75,11 @@ final class LinuxDebPackager extends LinuxPackager { try { // Try the real path first as it works better on newer Ubuntu versions - return findProvidingPackages(realPath, sysEnv.dpkg()); + return findProvidingPackages(realPath, sysEnv); } catch (IOException ex) { // Try the default path if differ if (!realPath.equals(file)) { - return findProvidingPackages(file, sysEnv.dpkg()); + return findProvidingPackages(file, sysEnv); } else { throw ex; } @@ -107,7 +106,7 @@ final class LinuxDebPackager extends LinuxPackager { properties.forEach(property -> cmdline.add(property.name)); - Map actualValues = Executor.of(cmdline.toArray(String[]::new)) + Map actualValues = Executor.of(cmdline) .saveOutput(true) .executeExpectSuccess() .getOutput().stream() @@ -148,8 +147,6 @@ final class LinuxDebPackager extends LinuxPackager { Path debFile = outputPackageFile(); - Log.verbose(I18N.format("message.outputting-to-location", debFile.toAbsolutePath())); - List cmdline = new ArrayList<>(); Stream.of(sysEnv.fakeroot(), sysEnv.dpkgdeb()).map(Path::toString).forEach(cmdline::add); if (Log.isVerbose()) { @@ -158,11 +155,8 @@ final class LinuxDebPackager extends LinuxPackager { cmdline.addAll(List.of("-b", env.appImageDir().toString(), debFile.toAbsolutePath().toString())); // run dpkg - RetryExecutor.retryOnKnownErrorMessage( - "semop(1): encountered an error: Invalid argument").execute( - cmdline.toArray(String[]::new)); - - Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath())); + Executor.of(cmdline).retryOnKnownErrorMessage( + "semop(1): encountered an error: Invalid argument").execute(); } @Override @@ -233,7 +227,7 @@ final class LinuxDebPackager extends LinuxPackager { } } - private static Stream findProvidingPackages(Path file, Path dpkg) throws IOException { + private static Stream findProvidingPackages(Path file, LinuxDebSystemEnvironment sysEnv) throws IOException { // // `dpkg -S` command does glob pattern lookup. If not the absolute path // to the file is specified it might return mltiple package names. @@ -279,9 +273,9 @@ final class LinuxDebPackager extends LinuxPackager { Set archPackages = new HashSet<>(); Set otherPackages = new HashSet<>(); - var debArch = LinuxPackageArch.getValue(LINUX_DEB); + var debArch = sysEnv.packageArch().value(); - Executor.of(dpkg.toString(), "-S", file.toString()) + Executor.of(sysEnv.dpkg().toString(), "-S", file.toString()) .saveOutput(true).executeExpectSuccess() .getOutput().forEach(line -> { Matcher matcher = PACKAGE_NAME_REGEX.matcher(line); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.java index 5b5decb7a67..d5480361452 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.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 @@ -28,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin; import jdk.jpackage.internal.util.Result; -public interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin { +interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin { static Result create(Result base) { return mixin(LinuxDebSystemEnvironment.class, base, LinuxDebSystemEnvironmentMixin::create); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java index 8688327b353..2cf3e9e36e8 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.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 @@ -29,7 +29,7 @@ import java.util.Objects; import java.util.stream.Stream; import jdk.jpackage.internal.util.Result; -public interface LinuxDebSystemEnvironmentMixin { +interface LinuxDebSystemEnvironmentMixin { Path dpkg(); Path dpkgdeb(); Path fakeroot(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java index 799c92ce2e1..0791c79c662 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.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 @@ -70,9 +70,9 @@ final class LinuxFromOptions { return LinuxApplication.create(appBuilder.create()); } - static LinuxRpmPackage createLinuxRpmPackage(Options options) { + static LinuxRpmPackage createLinuxRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) { - final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_RPM); + final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_RPM); final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder); @@ -81,9 +81,9 @@ final class LinuxFromOptions { return pkgBuilder.create(); } - static LinuxDebPackage createLinuxDebPackage(Options options) { + static LinuxDebPackage createLinuxDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) { - final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_DEB); + final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_DEB); final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder); @@ -99,7 +99,7 @@ final class LinuxFromOptions { return pkg; } - private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, StandardPackageType type) { + private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, LinuxSystemEnvironment sysEnv, StandardPackageType type) { final var app = createLinuxApplication(options); @@ -107,6 +107,8 @@ final class LinuxFromOptions { final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder); + pkgBuilder.arch(sysEnv.packageArch()); + LINUX_PACKAGE_DEPENDENCIES.ifPresentIn(options, pkgBuilder::additionalDependencies); LINUX_APP_CATEGORY.ifPresentIn(options, pkgBuilder::category); LINUX_MENU_GROUP.ifPresentIn(options, pkgBuilder::menuGroupName); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java index b14404d67b1..40ff26bcfac 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.util.Enquoter; /** * Helper to install launchers as services using "systemd". diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.java index 836d1fb2c37..b1df92ae312 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.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,18 +25,20 @@ package jdk.jpackage.internal; import java.io.IOException; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import java.util.ArrayList; import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.Result; -final class LinuxPackageArch { +record LinuxPackageArch(String value) { - static String getValue(StandardPackageType pkgType) { + static Result create(StandardPackageType pkgType) { switch (pkgType) { case LINUX_RPM -> { - return RpmPackageArch.VALUE; + return rpm().map(LinuxPackageArch::new); } case LINUX_DEB -> { - return DebPackageArch.VALUE; + return deb().map(LinuxPackageArch::new); } default -> { throw new IllegalArgumentException(); @@ -44,62 +46,51 @@ final class LinuxPackageArch { } } - private static class DebPackageArch { - - static final String VALUE = toSupplier(DebPackageArch::getValue).get(); - - private static String getValue() throws IOException { - return Executor.of("dpkg", "--print-architecture").saveOutput(true) - .executeExpectSuccess().getOutput().get(0); - } + private static Result deb() { + var exec = Executor.of("dpkg", "--print-architecture").saveOutput(true); + return Result.of(exec::executeExpectSuccess, IOException.class) + .flatMap(LinuxPackageArch::getStdoutFirstLine); } - private static class RpmPackageArch { - - /* - * Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for - * rpm packaging, try it first. rpm is optional and may not be available, use as the last - * resort. - */ - private static enum RpmArchReader { - Rpmbuild("rpmbuild", "--eval=%{_target_cpu}"), - Rpm("rpm", "--eval=%{_target_cpu}"); - - RpmArchReader(String... cmdline) { - this.cmdline = cmdline; + private static Result rpm() { + var errors = new ArrayList(); + for (var tool : RpmArchReader.values()) { + var result = tool.getRpmArch(); + if (result.hasValue()) { + return result; + } else { + errors.addAll(result.errors()); } - - String getRpmArch() throws IOException { - Executor exec = Executor.of(cmdline).saveOutput(true); - switch (this) { - case Rpm -> { - exec.executeExpectSuccess(); - } - case Rpmbuild -> { - if (exec.execute() != 0) { - return null; - } - } - default -> { - throw new UnsupportedOperationException(); - } - } - return exec.getOutput().get(0); - } - - private final String[] cmdline; } - static final String VALUE = toSupplier(RpmPackageArch::getValue).get(); + return Result.ofErrors(errors); + } - private static String getValue() throws IOException { - for (var rpmArchReader : RpmArchReader.values()) { - var rpmArchStr = rpmArchReader.getRpmArch(); - if (rpmArchStr != null) { - return rpmArchStr; - } - } - throw new RuntimeException("error.rpm-arch-not-detected"); + /* + * Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for + * rpm packaging, try it first. rpm is optional and may not be available, use as the last + * resort. + */ + private enum RpmArchReader { + RPMBUILD("rpmbuild", "--eval=%{_target_cpu}"), + RPM("rpm", "--eval=%{_target_cpu}"); + + RpmArchReader(String... cmdline) { + this.cmdline = cmdline; } + + Result getRpmArch() { + var exec = Executor.of(cmdline).saveOutput(true); + return Result.of(exec::executeExpectSuccess, IOException.class) + .flatMap(LinuxPackageArch::getStdoutFirstLine); + } + + private final String[] cmdline; + } + + private static Result getStdoutFirstLine(CommandOutputControl.Result result) { + return Result.of(() -> { + return result.stdout().stream().findFirst().orElseThrow(result::unexpected); + }, IOException.class); } } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java index bc7c301ace2..cd4d674432e 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.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 @@ -83,7 +83,7 @@ final class LinuxPackageBuilder { category(), Optional.ofNullable(additionalDependencies), release(), - pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow())); + arch.value())); } LinuxPackageBuilder literalName(String v) { @@ -119,6 +119,11 @@ final class LinuxPackageBuilder { return Optional.ofNullable(release); } + LinuxPackageBuilder arch(LinuxPackageArch v) { + arch = v; + return this; + } + private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String packageName) { final var lib = prefix.resolve(Path.of("lib", packageName)); return LinuxApplicationLayout.create( @@ -184,6 +189,7 @@ final class LinuxPackageBuilder { private String category; private String additionalDependencies; private String release; + private LinuxPackageArch arch; private final PackageBuilder pkgBuilder; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.java index 60355d0d1a2..e22b9c24fdd 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.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 @@ -133,8 +133,6 @@ final class LinuxRpmPackager extends LinuxPackager { Path rpmFile = outputPackageFile(); - Log.verbose(I18N.format("message.outputting-bundle-location", rpmFile.getParent())); - //run rpmbuild Executor.of(sysEnv.rpmbuild().toString(), "-bb", specFile().toAbsolutePath().toString(), @@ -147,8 +145,6 @@ final class LinuxRpmPackager extends LinuxPackager { env.buildRoot().toAbsolutePath()), "--define", String.format("%%_rpmfilename %s", rpmFile.getFileName()) ).executeExpectSuccess(); - - Log.verbose(I18N.format("message.output-bundle-location", rpmFile.getParent())); } private Path installPrefix() { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.java index 58c10668227..e56551be325 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.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 @@ -28,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin; import jdk.jpackage.internal.util.Result; -public interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin { +interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin { static Result create(Result base) { return mixin(LinuxRpmSystemEnvironment.class, base, LinuxRpmSystemEnvironmentMixin::create); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java index b741495f5ed..4cbd3ce4a9c 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.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 @@ -32,7 +32,7 @@ import java.util.stream.Stream; import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.util.Result; -public interface LinuxRpmSystemEnvironmentMixin { +interface LinuxRpmSystemEnvironmentMixin { Path rpm(); Path rpmbuild(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.java index 1a70cc938b8..e347c58ae21 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.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,6 @@ package jdk.jpackage.internal; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import jdk.jpackage.internal.model.PackageType; @@ -35,9 +34,10 @@ import jdk.jpackage.internal.model.StandardPackageType; import jdk.jpackage.internal.util.CompositeProxy; import jdk.jpackage.internal.util.Result; -public interface LinuxSystemEnvironment extends SystemEnvironment { +interface LinuxSystemEnvironment extends SystemEnvironment { boolean soLookupAvailable(); PackageType nativePackageType(); + LinuxPackageArch packageArch(); static Result create() { return detectNativePackageType().map(LinuxSystemEnvironment::create).orElseGet(() -> { @@ -45,7 +45,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment { }); } - static Optional detectNativePackageType() { + static Optional detectNativePackageType() { if (Internal.isDebian()) { return Optional.of(StandardPackageType.LINUX_DEB); } else if (Internal.isRpm()) { @@ -55,13 +55,14 @@ public interface LinuxSystemEnvironment extends SystemEnvironment { } } - static Result create(PackageType nativePackageType) { - return Result.ofValue(new Stub(LibProvidersLookup.supported(), - Objects.requireNonNull(nativePackageType))); + static Result create(StandardPackageType nativePackageType) { + return LinuxPackageArch.create(nativePackageType).map(arch -> { + return new Stub(LibProvidersLookup.supported(), nativePackageType, arch); + }); } static U createWithMixin(Class type, LinuxSystemEnvironment base, T mixin) { - return CompositeProxy.create(type, base, mixin); + return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(type, base, mixin); } static Result mixin(Class type, @@ -79,7 +80,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment { } } - record Stub(boolean soLookupAvailable, PackageType nativePackageType) implements LinuxSystemEnvironment { + record Stub(boolean soLookupAvailable, PackageType nativePackageType, LinuxPackageArch packageArch) implements LinuxSystemEnvironment { } static final class Internal { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties index 3aabe1f4ba5..3aa0e0e92a0 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.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 @@ -49,11 +49,7 @@ error.rpm-arch-not-detected="Failed to detect RPM arch" message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. message.test-for-tool=Test for [{0}]. Result: {1} -message.outputting-to-location=Generating DEB for installer to: {0}. -message.output-to-location=Package (.deb) saved to: {0}. message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. -message.outputting-bundle-location=Generating RPM for installer to: {0}. -message.output-bundle-location=Package (.rpm) saved to: {0}. message.ldd-not-available=ldd command not found. Package dependencies will not be generated. message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java index 4787d1297bb..602e147a970 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.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,8 +24,6 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -50,7 +48,7 @@ record AppImageInfoPListFile(String bundleIdentifier, String bundleName, String static AppImageInfoPListFile loadFromInfoPList(Path infoPListFile) throws IOException, InvalidPlistFileException, SAXException { - final var plistReader = new PListReader(initDocumentBuilder().parse(Files.newInputStream(infoPListFile))); + final var plistReader = new PListReader(Files.readAllBytes(infoPListFile)); try { return new AppImageInfoPListFile( diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index 71f87dd8705..4c5edf43627 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.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 @@ -47,7 +47,9 @@ import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.PathUtils; +import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -188,11 +190,9 @@ final class AppImageSigner { } private static boolean isXcodeDevToolsInstalled() { - try { - return Executor.of("/usr/bin/xcrun", "--help").setQuiet(true).execute() == 0; - } catch (IOException ex) { - return false; - } + return Result.of( + Executor.of("/usr/bin/xcrun", "--help").setQuiet(true)::executeExpectSuccess, + IOException.class).hasValue(); } private static void unsign(Path path) throws IOException { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java index 920b75df398..a7cd17b06b9 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.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 @@ -34,7 +34,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.stream.Stream; public final class Codesign { @@ -94,14 +93,12 @@ public final class Codesign { public void applyTo(Path path) throws IOException, CodesignException { - var exec = Executor.of(Stream.concat( - cmdline.stream(), - Stream.of(path.toString())).toArray(String[]::new) - ).saveOutput(true); + var exec = Executor.of(cmdline).args(path.toString()).saveOutput(true); configureExecutor.ifPresent(configure -> configure.accept(exec)); - if (exec.execute() != 0) { - throw new CodesignException(exec.getOutput().toArray(String[]::new)); + var result = exec.execute(); + if (result.getExitCode() != 0) { + throw new CodesignException(result.getOutput().toArray(String[]::new)); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java index 3cecb2cd243..224ea20f249 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,6 @@ import java.util.Optional; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.util.Result; public class MacBundlingEnvironment extends DefaultBundlingEnvironment { @@ -45,7 +44,7 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { .defaultOperation(CREATE_MAC_DMG) .bundler(SIGN_MAC_APP_IMAGE, MacBundlingEnvironment::signAppImage) .bundler(CREATE_MAC_APP_IMAGE, MacBundlingEnvironment::createAppImage) - .bundler(CREATE_MAC_DMG, LazyLoad::dmgSysEnv, MacBundlingEnvironment::createDmdPackage) + .bundler(CREATE_MAC_DMG, MacDmgSystemEnvironment::create, MacBundlingEnvironment::createDmdPackage) .bundler(CREATE_MAC_PKG, MacBundlingEnvironment::createPkgPackage)); } @@ -55,7 +54,6 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { buildEnv()::create, MacBundlingEnvironment::buildPipeline, (env, pkg, outputDir) -> { - Log.verbose(I18N.format("message.building-dmg", pkg.app().name())); return new MacDmgPackager(env, pkg, outputDir, sysEnv); }); } @@ -65,10 +63,7 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { MacFromOptions.createMacPkgPackage(options), buildEnv()::create, MacBundlingEnvironment::buildPipeline, - (env, pkg, outputDir) -> { - Log.verbose(I18N.format("message.building-pkg", pkg.app().name())); - return new MacPkgPackager(env, pkg, outputDir); - }); + MacPkgPackager::new); } private static void signAppImage(Options options) { @@ -98,13 +93,4 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { .predefinedAppImageLayout(APPLICATION_LAYOUT) .predefinedRuntimeImageLayout(MacPackage::guessRuntimeLayout); } - - private static final class LazyLoad { - - static Result dmgSysEnv() { - return DMG_SYS_ENV; - } - - private static final Result DMG_SYS_ENV = MacDmgSystemEnvironment.create(); - } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.java index fe593e347fc..24a236ae15d 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.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 @@ -53,7 +53,7 @@ public final class MacCertificateUtils { keychain.map(Keychain::asCliArg).ifPresent(args::add); return toSupplier(() -> { - final var output = Executor.of(args.toArray(String[]::new)) + final var output = Executor.of(args) .setQuiet(true).saveOutput(true).executeExpectSuccess() .getOutput(); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index 4ccc459109f..82bb9fc4dad 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.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,11 +33,15 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacDmgPackage; @@ -105,6 +109,10 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, return env.configDir().resolve(pkg.app().name() + "-license.plist"); } + private Path finalDmg() { + return outputDir.resolve(pkg.packageFileNameWithSuffix()); + } + Path protoDmg() { return dmgWorkdir().resolve("proto.dmg"); } @@ -128,6 +136,10 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } } + private Executor hdiutil(String... args) { + return Executor.of(sysEnv.hdiutil().toString()).args(args).storeOutputInFiles(); + } + private void prepareDMGSetupScript() throws IOException { Path dmgSetup = volumeScript(); Log.verbose(MessageFormat.format( @@ -211,43 +223,32 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } } + private String hdiUtilVerbosityFlag() { + return env.verbose() ? "-verbose" : "-quiet"; + } + private void buildDMG() throws IOException { boolean copyAppImage = false; - Path protoDMG = protoDmg(); - Path finalDMG = outputDir.resolve(pkg.packageFileNameWithSuffix()); + final Path protoDMG = protoDmg(); + final Path finalDMG = finalDmg(); - Path srcFolder = env.appImageDir(); - - Log.verbose(MessageFormat.format(I18N.getString( - "message.creating-dmg-file"), finalDMG.toAbsolutePath())); - - try { - Files.deleteIfExists(finalDMG); - } catch (IOException ex) { - throw new IOException(MessageFormat.format(I18N.getString( - "message.dmg-cannot-be-overwritten"), - finalDMG.toAbsolutePath())); - } + final Path srcFolder = env.appImageDir(); Files.createDirectories(protoDMG.getParent()); Files.createDirectories(finalDMG.getParent()); - String hdiUtilVerbosityFlag = env.verbose() ? - "-verbose" : "-quiet"; + final String hdiUtilVerbosityFlag = hdiUtilVerbosityFlag(); // create temp image - ProcessBuilder pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "create", - hdiUtilVerbosityFlag, - "-srcfolder", normalizedAbsolutePathString(srcFolder), - "-volname", volumeName(), - "-ov", normalizedAbsolutePathString(protoDMG), - "-fs", "HFS+", - "-format", "UDRW"); try { - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); + hdiutil("create", + hdiUtilVerbosityFlag, + "-srcfolder", normalizedAbsolutePathString(srcFolder), + "-volname", volumeName(), + "-ov", normalizedAbsolutePathString(protoDMG), + "-fs", "HFS+", + "-format", "UDRW").executeExpectSuccess(); } catch (IOException ex) { Log.verbose(ex); // Log exception @@ -260,31 +261,26 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, // not be bigger, but it will able to hold additional 50 megabytes of data. // We need extra room for icons and background image. When we providing // actual files to hdiutil, it will create DMG with ~50 megabytes extra room. - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "create", - hdiUtilVerbosityFlag, - "-size", String.valueOf(size), - "-volname", volumeName(), - "-ov", normalizedAbsolutePathString(protoDMG), - "-fs", "HFS+"); - new RetryExecutor() - .setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(3000) - .setWriteOutputToFile(true) - .execute(pb); + hdiutil( + "create", + hdiUtilVerbosityFlag, + "-size", String.valueOf(size), + "-volname", volumeName(), + "-ov", normalizedAbsolutePathString(protoDMG), + "-fs", "HFS+" + ).retry() + .setMaxAttemptsCount(10) + .setAttemptTimeout(3, TimeUnit.SECONDS) + .execute(); } + final Path mountedVolume = volumePath(); + // mount temp image - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "attach", + hdiutil("attach", normalizedAbsolutePathString(protoDMG), hdiUtilVerbosityFlag, - "-mountroot", protoDMG.getParent().toString()); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); - - final Path mountedVolume = volumePath(); + "-mountroot", mountedVolume.getParent().toString()).executeExpectSuccess(); // Copy app image, since we did not create DMG with it, but instead we created // empty one. @@ -302,9 +298,13 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, // to install-dir in DMG as critical error, since it can fail in // headless environment. try { - pb = new ProcessBuilder(sysEnv.osascript().toString(), - normalizedAbsolutePathString(volumeScript())); - IOUtils.exec(pb, 180); // Wait 3 minutes. See JDK-8248248. + Executor.of( + sysEnv.osascript().toString(), + normalizedAbsolutePathString(volumeScript()) + ) + // Wait 3 minutes. See JDK-8248248. + .timeout(3, TimeUnit.MINUTES) + .executeExpectSuccess(); } catch (IOException ex) { Log.verbose(ex); } @@ -325,18 +325,18 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, // but it seems Finder excepts these bytes to be // "icnC" for the volume icon // (might not work on Mac 10.13 with old XCode) - pb = new ProcessBuilder( + Executor.of( sysEnv.setFileUtility().orElseThrow().toString(), "-c", "icnC", - normalizedAbsolutePathString(volumeIconFile)); - IOUtils.exec(pb); + normalizedAbsolutePathString(volumeIconFile) + ).executeExpectSuccess(); volumeIconFile.toFile().setReadOnly(); - pb = new ProcessBuilder( + Executor.of( sysEnv.setFileUtility().orElseThrow().toString(), "-a", "C", - normalizedAbsolutePathString(mountedVolume)); - IOUtils.exec(pb); + normalizedAbsolutePathString(mountedVolume) + ).executeExpectSuccess(); } catch (IOException ex) { Log.error(ex.getMessage()); Log.verbose("Cannot enable custom icon using SetFile utility"); @@ -347,85 +347,23 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } finally { // Detach the temporary image - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "detach", - hdiUtilVerbosityFlag, - normalizedAbsolutePathString(mountedVolume)); - // "hdiutil detach" might not work right away due to resource busy error, so - // repeat detach several times. - RetryExecutor retryExecutor = new RetryExecutor(); - // Image can get detach even if we got resource busy error, so stop - // trying to detach it if it is no longer attached. - retryExecutor.setExecutorInitializer(exec -> { - if (!Files.exists(mountedVolume)) { - retryExecutor.abort(); - } - }); - try { - // 10 times with 6 second delays. - retryExecutor.setMaxAttemptsCount(10).setAttemptTimeoutMillis(6000) - .execute(pb); - } catch (IOException ex) { - if (!retryExecutor.isAborted()) { - // Now force to detach if it still attached - if (Files.exists(mountedVolume)) { - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "detach", - "-force", - hdiUtilVerbosityFlag, - normalizedAbsolutePathString(mountedVolume)); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); - } - } - } + detachVolume(); } // Compress it to a new image - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "convert", - normalizedAbsolutePathString(protoDMG), - hdiUtilVerbosityFlag, - "-format", "UDZO", - "-o", normalizedAbsolutePathString(finalDMG)); - try { - new RetryExecutor() - .setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(3000) - .execute(pb); - } catch (Exception ex) { - // Convert might failed if something holds file. Try to convert copy. - Path protoCopyDMG = protoCopyDmg(); - Files.copy(protoDMG, protoCopyDMG); - try { - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "convert", - normalizedAbsolutePathString(protoCopyDMG), - hdiUtilVerbosityFlag, - "-format", "UDZO", - "-o", normalizedAbsolutePathString(finalDMG)); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); - } finally { - Files.deleteIfExists(protoCopyDMG); - } - } + convertProtoDmg(); //add license if needed if (pkg.licenseFile().isPresent()) { - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), + hdiutil( "udifrez", normalizedAbsolutePathString(finalDMG), "-xml", normalizedAbsolutePathString(licenseFile()) - ); - new RetryExecutor() - .setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(3000) - .execute(pb); + ).retry() + .setMaxAttemptsCount(10) + .setAttemptTimeout(3, TimeUnit.SECONDS) + .execute(); } try { @@ -434,11 +372,69 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } catch (IOException ex) { // Don't care if fails } + } - Log.verbose(MessageFormat.format(I18N.getString( - "message.output-to-location"), - pkg.app().name(), normalizedAbsolutePathString(finalDMG))); + private void detachVolume() throws IOException { + var mountedVolume = volumePath(); + // "hdiutil detach" might not work right away due to resource busy error, so + // repeat detach several times. + Globals.instance().objectFactory().retryExecutor(IOException.class).setExecutable(context -> { + + List cmdline = new ArrayList<>(); + cmdline.add("detach"); + + if (context.isLastAttempt()) { + // The last attempt, force detach. + cmdline.add("-force"); + } + + cmdline.addAll(List.of( + hdiUtilVerbosityFlag(), + normalizedAbsolutePathString(mountedVolume) + )); + + // The image can get detached even if we get a resource busy error, + // so execute the detach command without checking the exit code. + var result = hdiutil(cmdline.toArray(String[]::new)).execute(); + + if (result.getExitCode() == 0 || !Files.exists(mountedVolume)) { + // Detached successfully! + return null; + } else { + throw result.unexpected(); + } + }).setMaxAttemptsCount(10).setAttemptTimeout(6, TimeUnit.SECONDS).execute(); + } + + private void convertProtoDmg() throws IOException { + + Function convert = srcDmg -> { + return hdiutil( + "convert", + normalizedAbsolutePathString(srcDmg), + hdiUtilVerbosityFlag(), + "-format", "UDZO", + "-o", normalizedAbsolutePathString(finalDmg())); + }; + + // Convert it to a new image. + try { + convert.apply(protoDmg()).retry() + .setMaxAttemptsCount(10) + .setAttemptTimeout(3, TimeUnit.SECONDS) + .execute(); + } catch (IOException ex) { + Log.verbose(ex); + // Something holds the file, try to convert a copy. + Path copyDmg = protoCopyDmg(); + Files.copy(protoDmg(), copyDmg); + try { + convert.apply(copyDmg).executeExpectSuccess(); + } finally { + Files.deleteIfExists(copyDmg); + } + } } // Background image name in resources diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java index 54eb0c6f4fe..12d105b99b5 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.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,10 +25,11 @@ package jdk.jpackage.internal; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.Result; @@ -54,41 +55,31 @@ record MacDmgSystemEnvironment(Path hdiutil, Path osascript, Optional setF // Location of SetFile utility may be different depending on MacOS version // We look for several known places and if none of them work will // try to find it - private static Optional findSetFileUtility() { - String typicalPaths[] = {"/Developer/Tools/SetFile", - "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"}; + static Optional findSetFileUtility() { + return SETFILE_KNOWN_PATHS.stream().filter(setFilePath -> { + // Validate SetFile, if Xcode is not installed it will run, but exit with error code + return Result.of( + Executor.of(setFilePath.toString(), "-h").setQuiet(true)::executeExpectSuccess, + IOException.class).hasValue(); + }).findFirst().or(() -> { + // generic find attempt + final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile").setQuiet(true).saveFirstLineOfOutput(); - final var setFilePath = Stream.of(typicalPaths).map(Path::of).filter(Files::isExecutable).findFirst(); - if (setFilePath.isPresent()) { - // Validate SetFile, if Xcode is not installed it will run, but exit with error - // code - try { - if (Executor.of(setFilePath.orElseThrow().toString(), "-h").setQuiet(true).execute() == 0) { - return setFilePath; - } - } catch (Exception ignored) { - // No need for generic find attempt. We found it, but it does not work. - // Probably due to missing xcode. - return Optional.empty(); - } - } - - // generic find attempt - try { - final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile"); - final var code = executor.setQuiet(true).saveOutput(true).execute(); - if (code == 0 && !executor.getOutput().isEmpty()) { - final var firstLine = executor.getOutput().getFirst(); - Path f = Path.of(firstLine); - if (new ToolValidator(f).checkExistsOnly().validate() == null) { - return Optional.of(f.toAbsolutePath()); - } - } - } catch (IOException ignored) {} - - return Optional.empty(); + return Result.of(executor::executeExpectSuccess, IOException.class).flatMap(execResult -> { + return Result.of(() -> { + return execResult.stdout().stream().findFirst().map(Path::of).orElseThrow(execResult::unexpected); + }, Exception.class); + }).value().filter(v -> { + return new ToolValidator(v).checkExistsOnly().validate() == null; + }).map(Path::toAbsolutePath); + }); } + static final List SETFILE_KNOWN_PATHS = Stream.of( + "/Developer/Tools/SetFile", + "/usr/bin/SetFile", + "/Developer/usr/bin/SetFile").map(Path::of).collect(Collectors.toUnmodifiableList()); + private static final Path HDIUTIL = Path.of("/usr/bin/hdiutil"); private static final Path OSASCRIPT = Path.of("/usr/bin/osascript"); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index b1094331740..cdf33d6dcba 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.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 @@ -30,8 +30,8 @@ import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasJliLib; import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasNoBinDir; import static jdk.jpackage.internal.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; -import static jdk.jpackage.internal.cli.StandardOption.ICON; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; +import static jdk.jpackage.internal.cli.StandardOption.ICON; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_CATEGORY; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_IMAGE_SIGN_IDENTITY; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_STORE; @@ -52,11 +52,13 @@ import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; import java.nio.file.Path; +import java.util.List; import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.ApplicationBuilder.MainLauncherStartupInfo; import jdk.jpackage.internal.SigningIdentityBuilder.ExpiredCertificateException; import jdk.jpackage.internal.SigningIdentityBuilder.StandardCertificateSelector; +import jdk.jpackage.internal.cli.OptionValue; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardFaOption; import jdk.jpackage.internal.model.ApplicationLaunchers; @@ -71,6 +73,7 @@ import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -276,16 +279,12 @@ final class MacFromOptions { final var builder = new MacPackageBuilder(createPackageBuilder(options, app.app(), type)); - app.externalApp() - .map(ExternalApplication::extra) - .flatMap(MAC_SIGN::findIn) - .ifPresent(builder::predefinedAppImageSigned); - - PREDEFINED_RUNTIME_IMAGE.findIn(options) - .map(MacBundle::new) - .filter(MacBundle::isValid) - .map(MacBundle::isSigned) - .ifPresent(builder::predefinedAppImageSigned); + for (OptionValue ov : List.of(PREDEFINED_APP_IMAGE, PREDEFINED_RUNTIME_IMAGE)) { + ov.findIn(options) + .flatMap(MacBundle::fromPath) + .map(MacPackagingPipeline::isSigned) + .ifPresent(builder::predefinedAppImageSigned); + } return builder; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index 53f297282ba..4e63f6db178 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.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 @@ -76,6 +76,8 @@ import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; @@ -178,13 +180,10 @@ final class MacPackagingPipeline { builder.task(MacCopyAppImageTaskID.COPY_RUNTIME_JLILIB) .appImageAction(MacPackagingPipeline::copyJliLib).add(); - final var predefinedRuntimeBundle = Optional.of( - new MacBundle(p.predefinedAppImage().orElseThrow())).filter(MacBundle::isValid); - // Don't create ".package" file. disabledTasks.add(MacCopyAppImageTaskID.COPY_PACKAGE_FILE); - if (predefinedRuntimeBundle.isPresent()) { + if (MacBundle.fromPath(p.predefinedAppImage().orElseThrow()).isPresent()) { // The input runtime image is a macOS bundle. // Disable all alterations of the input bundle, but keep the signing enabled. disabledTasks.addAll(List.of(MacCopyAppImageTaskID.values())); @@ -195,7 +194,7 @@ final class MacPackagingPipeline { .appImageAction(MacPackagingPipeline::writeRuntimeInfoPlist).add(); } - if (predefinedRuntimeBundle.map(MacBundle::isSigned).orElse(false) && !((MacPackage)p).app().sign()) { + if (((MacPackage)p).predefinedAppImageSigned().orElse(false) && !((MacPackage)p).app().sign()) { // The input runtime is a signed bundle; explicit signing is not requested for the package. // Disable the signing, i.e. don't re-sign the input bundle. disabledTasks.add(MacCopyAppImageTaskID.COPY_SIGN); @@ -218,6 +217,11 @@ final class MacPackagingPipeline { enum SignAppImagePackageType implements PackageType { VALUE; + + @Override + public String label() { + throw new UnsupportedOperationException(); + } } static Package createSignAppImagePackage(MacApplication app, BuildEnv env) { @@ -279,6 +283,30 @@ final class MacPackagingPipeline { } } + static boolean isSigned(MacBundle bundle) { + + var result = toSupplier(Executor.of( + "/usr/sbin/spctl", + "-vv", + "--raw", + "--assess", + "--type", "exec", + bundle.root().toString()).setQuiet(true).saveOutput(true).binaryOutput()::execute).get(); + + switch (result.getExitCode()) { + case 0, 3 -> { + // These exit codes are accompanied with valid plist xml. + return toSupplier(() -> { + return new PListReader(result.byteStdout()).findValue("assessment:originator").isPresent(); + }).get(); + } + default -> { + // Likely to be an "a sealed resource is missing or invalid" error. + return false; + } + } + } + private static void copyAppImage(MacPackage pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException { @@ -286,7 +314,7 @@ final class MacPackagingPipeline { final Optional srcMacBundle; if (pkg.isRuntimeInstaller()) { - srcMacBundle = MacBundle.fromAppImageLayout(srcAppImage); + srcMacBundle = macBundleFromAppImageLayout(srcAppImage); } else { srcMacBundle = Optional.empty(); } @@ -297,7 +325,7 @@ final class MacPackagingPipeline { try { FileUtils.copyRecursive( inputBundle.root(), - MacBundle.fromAppImageLayout(dstAppImage).orElseThrow().root(), + macBundleFromAppImageLayout(dstAppImage).orElseThrow().root(), LinkOption.NOFOLLOW_LINKS); } catch (IOException ex) { throw new UncheckedIOException(ex); @@ -415,7 +443,7 @@ final class MacPackagingPipeline { final var app = env.app(); - final var infoPlistFile = MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow().infoPlistFile(); + final var infoPlistFile = macBundleFromAppImageLayout(env.resolvedLayout()).orElseThrow().infoPlistFile(); Log.verbose(I18N.format("message.preparing-info-plist", PathUtils.normalizedAbsolutePathString(infoPlistFile))); @@ -468,7 +496,7 @@ final class MacPackagingPipeline { } final Runnable signAction = () -> { - AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow()); + AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(macBundleFromAppImageLayout(env.resolvedLayout()).orElseThrow()); }; app.signingConfig().flatMap(AppImageSigningConfig::keychain).map(Keychain::new).ifPresentOrElse(keychain -> { @@ -550,7 +578,7 @@ final class MacPackagingPipeline { private static MacBundle runtimeBundle(AppImageBuildEnv env) { if (env.app().isRuntime()) { - return MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow(); + return macBundleFromAppImageLayout(env.resolvedLayout()).orElseThrow(); } else { return new MacBundle(((MacApplicationLayout)env.resolvedLayout()).runtimeRootDirectory()); } @@ -595,6 +623,22 @@ final class MacPackagingPipeline { }; } + private static Optional macBundleFromAppImageLayout(AppImageLayout layout) { + final var root = layout.rootDirectory(); + final var bundleSubdir = root.relativize(layout.runtimeDirectory()); + final var contentsDirname = Path.of("Contents"); + var bundleRoot = root; + for (int i = 0; i != bundleSubdir.getNameCount(); i++) { + var nameComponent = bundleSubdir.getName(i); + if (contentsDirname.equals(nameComponent)) { + return Optional.of(new MacBundle(bundleRoot)); + } else { + bundleRoot = bundleRoot.resolve(nameComponent); + } + } + return Optional.empty(); + } + private record TaskContextProxy(TaskContext delegate, boolean forApp, boolean copyAppImage) implements TaskContext { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java index 127b3232661..126248e2330 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.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 @@ -32,7 +32,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.text.MessageFormat; @@ -57,6 +56,7 @@ import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.resources.ResourceLocator; +import jdk.jpackage.internal.util.Enquoter; import jdk.jpackage.internal.util.XmlUtils; import org.xml.sax.SAXException; @@ -108,7 +108,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic cmdline.addAll(allPkgbuildArgs()); try { Files.createDirectories(path.getParent()); - IOUtils.exec(new ProcessBuilder(cmdline), false, null, true, Executor.INFINITE_TIMEOUT); + Executor.of(cmdline).executeExpectSuccess(); } catch (IOException ex) { throw new UncheckedIOException(ex); } @@ -487,15 +487,13 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic Files.createDirectories(cpl.getParent()); - final var pb = new ProcessBuilder("/usr/bin/pkgbuild", + Executor.of("/usr/bin/pkgbuild", "--root", normalizedAbsolutePathString(env.appImageDir()), "--install-location", normalizedAbsolutePathString(installLocation()), "--analyze", - normalizedAbsolutePathString(cpl)); - - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); + normalizedAbsolutePathString(cpl)).executeExpectSuccess(); patchCPLFile(cpl); } @@ -544,8 +542,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic } commandLine.add(normalizedAbsolutePathString(finalPkg)); - final var pb = new ProcessBuilder(commandLine); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); + Executor.of(commandLine).executeExpectSuccess(); } private static Optional createServices(BuildEnv env, MacPkgPackage pkg) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java index b38faecd96f..2f616aafba1 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.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,15 +27,17 @@ package jdk.jpackage.internal; import java.io.Closeable; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import jdk.internal.util.OSVersion; -import jdk.jpackage.internal.util.function.ThrowingConsumer; final class TempKeychain implements Closeable { - static void withKeychains(ThrowingConsumer, ? extends Exception> keychainConsumer, List keychains) throws Exception { + static void withKeychains(Consumer> keychainConsumer, List keychains) { + keychains.forEach(Objects::requireNonNull); if (keychains.isEmpty() || OSVersion.current().compareTo(new OSVersion(10, 12)) < 0) { keychainConsumer.accept(keychains); @@ -43,11 +45,14 @@ final class TempKeychain implements Closeable { // we need this for OS X 10.12+ try (var tempKeychain = new TempKeychain(keychains)) { keychainConsumer.accept(tempKeychain.keychains); + } catch (IOException ex) { + throw new UncheckedIOException(ex); } } } - static void withKeychain(ThrowingConsumer keychainConsumer, Keychain keychain) throws Exception { + static void withKeychain(Consumer keychainConsumer, Keychain keychain) { + Objects.requireNonNull(keychainConsumer); withKeychains(keychains -> { keychainConsumer.accept(keychains.getFirst()); @@ -78,7 +83,7 @@ final class TempKeychain implements Closeable { args.addAll(missingKeychains.stream().map(Keychain::asCliArg).toList()); - Executor.of(args.toArray(String[]::new)).executeExpectSuccess(); + Executor.of(args).executeExpectSuccess(); } } @@ -89,7 +94,7 @@ final class TempKeychain implements Closeable { @Override public void close() throws IOException { if (!restoreKeychainsCmd.isEmpty()) { - Executor.of(restoreKeychainsCmd.toArray(String[]::new)).executeExpectSuccess(); + Executor.of(restoreKeychainsCmd).executeExpectSuccess(); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java index cfe10e8a012..e2b3d30b7ae 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.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 @@ -28,7 +28,6 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toUnmodifiableMap; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_APP_STORE; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_MAIN_CLASS; -import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_SIGNED; import java.nio.file.Path; import java.util.Map; @@ -96,9 +95,6 @@ public interface MacApplication extends Application, MacApplicationMixin { } public enum ExtraAppImageFileField { - SIGNED(MAC_SIGNED, app -> { - return Optional.of(Boolean.toString(app.sign())); - }), APP_STORE(MAC_APP_STORE, app -> { return Optional.of(Boolean.toString(app.appStore())); }), diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index 240e82dcc9b..ceeab587f66 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -57,12 +57,7 @@ message.version-string-first-number-not-zero=The first number in an app-version message.keychain.error=Unable to get keychain list. message.invalid-identifier=Invalid mac bundle identifier [{0}]. message.invalid-identifier.advice=specify identifier with "--mac-package-identifier". -message.building-dmg=Building DMG package for {0}. message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists [{0}] and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. message.preparing-scripts=Preparing package scripts. message.preparing-distribution-dist=Preparing distribution.dist: {0}. message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java index 5f473b554be..75ead9d08ad 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,11 +28,12 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toUnmodifiableMap; import static java.util.stream.Collectors.toUnmodifiableSet; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.APP_VERSION; -import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.DESCRIPTION; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_NAME; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; @@ -162,7 +163,23 @@ final class AppImageFile { final var relativeAppImageFilePath = appImageDir.relativize(appImageFilePath); try { - final Document doc = XmlUtils.initDocumentBuilder().parse(Files.newInputStream(appImageFilePath)); + // + // Use javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream). + // Don't use javax.xml.parsers.DocumentBuilder#parse(java.io.File) as this will introduce + // dependency on how the XML parser reports filesystem I/O errors. + // E.g.: the default JDK XML parser throws java.io.FileNotFoundException if the supplied + // directory is not found and throws org.xml.sax.SAXParseException if the supplied file is a directory. + // Another DOM XML parser (a different version of Xerces?) may behave differently. + // + // The use of javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream) eliminates + // differences in how XML parsers handle file system I/O errors. + // Filesystem I/O is delegated to java.nio.file.Files#readAllBytes(java.nio.file.Path), + // XML parser deals with the byte stream in memory and the error handling code + // doesn't depend on how XML parser reports filesystem I/O errors because + // it reads data from memory, not from the filesystem. + // + final Document doc = XmlUtils.initDocumentBuilder().parse( + new ByteArrayInputStream(Files.readAllBytes(appImageFilePath))); final XPath xPath = XPathFactory.newInstance().newXPath(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index 05e080f240a..3a99dfb04da 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.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 @@ -42,17 +42,15 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.cli.CliBundlingEnvironment; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardBundlingOperation; -import jdk.jpackage.internal.model.AppImagePackageType; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.model.PackageType; -import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.Result; class DefaultBundlingEnvironment implements CliBundlingEnvironment { @@ -65,10 +63,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Map>>> bundlers) { this.bundlers = bundlers.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> { - return new CachingSupplier<>(e.getValue()); + return runOnce(e.getValue()); })); - this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(CachingSupplier::new); + this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(DefaultBundlingEnvironment::runOnce); } @@ -98,6 +96,11 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { return bundler(op, () -> Result.ofValue(bundler)); } + Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + private Supplier> defaultOperationSupplier; private final Map>>> bundlers = new HashMap<>(); } @@ -107,6 +110,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { return new Builder(); } + static Supplier runOnce(Supplier supplier) { + return new CachingSupplier<>(supplier); + } + static Supplier>> createBundlerSupplier( Supplier> sysEnvResultSupplier, BiConsumer bundler) { Objects.requireNonNull(sysEnvResultSupplier); @@ -125,7 +132,9 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Objects.requireNonNull(app); Objects.requireNonNull(pipelineBuilder); - final var outputDir = OptionUtils.outputDir(options).resolve(app.appImageDirName()); + final var outputDir = PathUtils.normalizedAbsolutePath(OptionUtils.outputDir(options).resolve(app.appImageDirName())); + + Log.verbose(I18N.getString("message.create-app-image")); IOUtils.writableOutputDir(outputDir.getParent()); @@ -133,14 +142,14 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { .predefinedAppImageLayout(app.asApplicationLayout().orElseThrow()) .create(options, app); - Log.verbose(I18N.format("message.creating-app-bundle", outputDir.getFileName(), outputDir.toAbsolutePath().getParent())); - if (Files.exists(outputDir)) { - throw new JPackageException(I18N.format("error.root-exists", outputDir.toAbsolutePath())); + throw new JPackageException(I18N.format("error.root-exists", outputDir)); } pipelineBuilder.excludeDirFromCopying(outputDir.getParent()) .create().execute(BuildEnv.withAppImageDir(env, outputDir), app); + + Log.verbose(I18N.getString("message.app-image-created")); } static void createNativePackage(Options options, @@ -165,11 +174,20 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Objects.requireNonNull(createPipelineBuilder); Objects.requireNonNull(pipelineBuilderMutatorFactory); + var pipelineBuilder = Objects.requireNonNull(createPipelineBuilder.apply(pkg)); + + // Delete an old output package file (if any) before creating a new one. + pipelineBuilder.task(PackageTaskID.DELETE_OLD_PACKAGE_FILE) + .addDependencies(pipelineBuilder.taskGraphSnapshot().getTailsOf(PackageTaskID.CREATE_PACKAGE_FILE)) + .addDependent(PackageTaskID.CREATE_PACKAGE_FILE) + .packageAction(PackagingPipeline::deleteOutputBundle) + .add(); + Packager.build().pkg(pkg) - .outputDir(OptionUtils.outputDir(options)) - .env(Objects.requireNonNull(createBuildEnv.apply(options, pkg))) - .pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory) - .execute(Objects.requireNonNull(createPipelineBuilder.apply(pkg))); + .outputDir(OptionUtils.outputDir(options)) + .env(Objects.requireNonNull(createBuildEnv.apply(options, pkg))) + .pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory) + .execute(pipelineBuilder); } @Override @@ -186,10 +204,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { permanentWorkDirectory = Optional.of(tempDir.path()); } bundler.accept(tempDir.options()); - - var packageType = OptionUtils.bundlingOperation(cmdline).packageType(); - - Log.verbose(I18N.format("message.bundle-created", I18N.getString(bundleTypeDescription(packageType, op.os())))); } catch (IOException ex) { throw new UncheckedIOException(ex); } finally { @@ -210,55 +224,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { }); } - private String bundleTypeDescription(PackageType type, OperatingSystem os) { - switch (type) { - case StandardPackageType stdType -> { - switch (stdType) { - case WIN_MSI -> { - return "bundle-type.win-msi"; - } - case WIN_EXE -> { - return "bundle-type.win-exe"; - } - case LINUX_DEB -> { - return "bundle-type.linux-deb"; - } - case LINUX_RPM -> { - return "bundle-type.linux-rpm"; - } - case MAC_DMG -> { - return "bundle-type.mac-dmg"; - } - case MAC_PKG -> { - return "bundle-type.mac-pkg"; - } - default -> { - throw new AssertionError(); - } - } - } - case AppImagePackageType appImageType -> { - switch (os) { - case WINDOWS -> { - return "bundle-type.win-app"; - } - case LINUX -> { - return "bundle-type.linux-app"; - } - case MACOS -> { - return "bundle-type.mac-app"; - } - default -> { - throw new AssertionError(); - } - } - } - default -> { - throw new AssertionError(); - } - } - } - private static final class CachingSupplier implements Supplier { @@ -279,5 +244,5 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { private final Map>>> bundlers; - private final Optional>> defaultOperationSupplier; + private final Optional>> defaultOperationSupplier; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java index dd1cc4a24b4..53c587ab37c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,53 +25,154 @@ package jdk.jpackage.internal; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.PrintStream; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; import java.util.stream.Stream; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; +import jdk.jpackage.internal.util.CommandLineFormat; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.ProcessAttributes; +import jdk.jpackage.internal.util.CommandOutputControl.Result; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.internal.util.function.ExceptionBox; -public final class Executor { +final class Executor { - Executor() { + static Executor of(String... cmdline) { + return of(List.of(cmdline)); } - Executor setOutputConsumer(Consumer> v) { - outputConsumer = v; - return this; + static Executor of(List cmdline) { + return of(new ProcessBuilder(cmdline)); + } + + static Executor of(ProcessBuilder pb) { + return Globals.instance().objectFactory().executor().processBuilder(pb); + } + + public Executor() { + commandOutputControl = new CommandOutputControl(); + args = new ArrayList<>(); + } + + private Executor(Executor other) { + commandOutputControl = other.commandOutputControl.copy(); + quietCommand = other.quietCommand; + args = new ArrayList<>(other.args); + processBuilder = other.processBuilder; + toolProvider = other.toolProvider; + timeout = other.timeout; + mapper = other.mapper; } Executor saveOutput(boolean v) { - saveOutput = v; + commandOutputControl.saveOutput(v); return this; } - Executor setWriteOutputToFile(boolean v) { - writeOutputToFile = v; + Executor saveOutput() { + return saveOutput(true); + } + + Executor saveFirstLineOfOutput() { + commandOutputControl.saveFirstLineOfOutput(); return this; } - Executor setTimeout(long v) { + Executor charset(Charset v) { + commandOutputControl.charset(v); + return this; + } + + Executor storeOutputInFiles(boolean v) { + commandOutputControl.storeOutputInFiles(v); + return this; + } + + Executor storeOutputInFiles() { + return storeOutputInFiles(true); + } + + Executor binaryOutput(boolean v) { + commandOutputControl.binaryOutput(v); + return this; + } + + Executor binaryOutput() { + return binaryOutput(true); + } + + Executor discardStdout(boolean v) { + commandOutputControl.discardStdout(v); + return this; + } + + Executor discardStdout() { + return discardStdout(true); + } + + Executor discardStderr(boolean v) { + commandOutputControl.discardStderr(v); + return this; + } + + Executor discardStderr() { + return discardStderr(true); + } + + Executor timeout(long v, TimeUnit unit) { + return timeout(Duration.of(v, unit.toChronoUnit())); + } + + Executor timeout(Duration v) { timeout = v; - if (timeout != INFINITE_TIMEOUT) { - // Redirect output to file if timeout is requested, otherwise we will - // reading until process ends and timeout will never be reached. - setWriteOutputToFile(true); - } return this; } - Executor setProcessBuilder(ProcessBuilder v) { - pb = v; + Executor toolProvider(ToolProvider v) { + toolProvider = Objects.requireNonNull(v); + processBuilder = null; return this; } - Executor setCommandLine(String... cmdline) { - return setProcessBuilder(new ProcessBuilder(cmdline)); + Optional toolProvider() { + return Optional.ofNullable(toolProvider); + } + + Executor processBuilder(ProcessBuilder v) { + processBuilder = Objects.requireNonNull(v); + toolProvider = null; + return this; + } + + Optional processBuilder() { + return Optional.ofNullable(processBuilder); + } + + Executor args(List v) { + args.addAll(v); + return this; + } + + Executor args(String... args) { + return args(List.of(args)); + } + + List args() { + return args; } Executor setQuiet(boolean v) { @@ -79,159 +180,202 @@ public final class Executor { return this; } - List getOutput() { - return output; - } - - Executor executeExpectSuccess() throws IOException { - int ret = execute(); - if (0 != ret) { - throw new IOException( - String.format("Command %s exited with %d code", - createLogMessage(pb, false), ret)); - } + Executor mapper(UnaryOperator v) { + mapper = v; return this; } - int execute() throws IOException { - output = null; + Optional> mapper() { + return Optional.ofNullable(mapper); + } - boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput; - Path outputFile = null; - if (needProcessOutput) { - pb.redirectErrorStream(true); - if (writeOutputToFile) { - outputFile = Files.createTempFile("jpackageOutputTempFile", ".tmp"); - pb.redirectOutput(outputFile.toFile()); + Executor copy() { + return new Executor(this); + } + + Result execute() throws IOException { + if (mapper != null) { + var mappedExecutor = Objects.requireNonNull(mapper.apply(this)); + if (mappedExecutor != this) { + return mappedExecutor.execute(); } + } + + var coc = commandOutputControl.copy(); + + final CommandOutputControl.Executable exec; + if (processBuilder != null) { + exec = coc.createExecutable(copyProcessBuilder()); + } else if (toolProvider != null) { + exec = coc.createExecutable(toolProvider, args.toArray(String[]::new)); } else { - // We are not going to read process output, so need to notify - // ProcessBuilder about this. Otherwise some processes might just - // hang up (`ldconfig -p`). - pb.redirectError(ProcessBuilder.Redirect.DISCARD); - pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); + throw new IllegalStateException("No target to execute"); } - if (!quietCommand) { - Log.verbose(String.format("Running %s", createLogMessage(pb, true))); - } - - Process p = pb.start(); - - int code = 0; - if (writeOutputToFile) { - try { - code = waitForProcess(p); - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } - } - - if (needProcessOutput) { - final List savedOutput; - Supplier> outputStream; - - if (writeOutputToFile) { - output = savedOutput = Files.readAllLines(outputFile); - Files.delete(outputFile); - outputStream = () -> { - if (savedOutput != null) { - return savedOutput.stream(); - } - return null; - }; - if (outputConsumer != null) { - outputConsumer.accept(outputStream.get()); - } - } else { - try (var br = new BufferedReader(new InputStreamReader( - p.getInputStream()))) { - - if ((outputConsumer != null || Log.isVerbose()) - || saveOutput) { - savedOutput = br.lines().toList(); - } else { - savedOutput = null; - } - output = savedOutput; - - outputStream = () -> { - if (savedOutput != null) { - return savedOutput.stream(); - } - return br.lines(); - }; - if (outputConsumer != null) { - outputConsumer.accept(outputStream.get()); - } - - if (savedOutput == null) { - // For some processes on Linux if the output stream - // of the process is opened but not consumed, the process - // would exit with code 141. - // It turned out that reading just a single line of process - // output fixes the problem, but let's process - // all of the output, just in case. - br.lines().forEach(x -> {}); - } - } - } + if (dumpOutput()) { + Log.verbose(String.format("Running %s", CommandLineFormat.DEFAULT.apply(List.of(commandLine().getFirst())))); } + var printableOutputBuilder = new PrintableOutputBuilder(coc); + Result result; try { - if (!writeOutputToFile) { - code = p.waitFor(); - } - if (!quietCommand) { - Log.verbose(pb.command(), getOutput(), code, IOUtils.getPID(p)); - } - return code; - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } - } - - private int waitForProcess(Process p) throws InterruptedException { - if (timeout == INFINITE_TIMEOUT) { - return p.waitFor(); - } else { - if (p.waitFor(timeout, TimeUnit.SECONDS)) { - return p.exitValue(); + if (timeout == null) { + result = exec.execute(); } else { - Log.verbose(String.format("Command %s timeout after %d seconds", - createLogMessage(pb, false), timeout)); - p.destroy(); - return -1; + result = exec.execute(timeout.toMillis(), TimeUnit.MILLISECONDS); + } + } catch (InterruptedException ex) { + throw ExceptionBox.toUnchecked(ex); + } + + var printableOutput = printableOutputBuilder.create(); + if (dumpOutput()) { + log(result, printableOutput); + } + + return ExecutableAttributesWithCapturedOutput.augmentResultWithOutput(result, printableOutput); + } + + Result executeExpectSuccess() throws IOException { + return execute().expectExitCode(0); + } + + Result executeExpect(int mainExitCode, int... otherExitCodes) throws IOException { + return execute().expectExitCode(mainExitCode, otherExitCodes); + } + + RetryExecutor retry() { + return Globals.instance().objectFactory().retryExecutor(IOException.class) + .setExecutable(this::executeExpectSuccess); + } + + RetryExecutor retryOnKnownErrorMessage(String msg) { + Objects.requireNonNull(msg); + return saveOutput().retry().setExecutable(() -> { + // Execute it without exit code check. + var result = execute(); + if (result.stderr().stream().anyMatch(msg::equals)) { + throw result.unexpected(); + } + return result; + }); + } + + List commandLine() { + if (processBuilder != null) { + return Stream.of(processBuilder.command(), args).flatMap(Collection::stream).toList(); + } else if (toolProvider != null) { + return Stream.concat(Stream.of(toolProvider.name()), args.stream()).toList(); + } else { + throw new IllegalStateException("No target to execute"); + } + } + + private ProcessBuilder copyProcessBuilder() { + if (processBuilder == null) { + throw new IllegalStateException(); + } + + var copy = new ProcessBuilder(commandLine()); + copy.directory(processBuilder.directory()); + var env = copy.environment(); + env.clear(); + env.putAll(processBuilder.environment()); + + return copy; + } + + private boolean dumpOutput() { + return Log.isVerbose() && !quietCommand; + } + + private static void log(Result result, String printableOutput) throws IOException { + Objects.requireNonNull(result); + Objects.requireNonNull(printableOutput); + + Optional pid; + if (result.execAttrs() instanceof ProcessAttributes attrs) { + pid = attrs.pid(); + } else { + pid = Optional.empty(); + } + + var sb = new StringBuilder(); + sb.append("Command"); + pid.ifPresent(p -> { + sb.append(" [PID: ").append(p).append("]"); + }); + sb.append(":\n ").append(result.execAttrs().printableCommandLine()); + Log.verbose(sb.toString()); + + if (!printableOutput.isEmpty()) { + sb.delete(0, sb.length()); + sb.append("Output:"); + try (var lines = new BufferedReader(new StringReader(printableOutput)).lines()) { + lines.forEach(line -> { + sb.append("\n ").append(line); + }); + } + Log.verbose(sb.toString()); + } + + result.exitCode().ifPresentOrElse(exitCode -> { + Log.verbose("Returned: " + exitCode + "\n"); + }, () -> { + Log.verbose("Aborted: timed-out" + "\n"); + }); + } + + private static final class PrintableOutputBuilder { + + PrintableOutputBuilder(CommandOutputControl coc) { + coc.dumpOutput(true); + charset = coc.charset(); + if (coc.isBinaryOutput()) { + // Assume binary output goes into stdout and text error messages go into stderr, so keep them separated. + sinks = new ByteArrayOutputStream[2]; + sinks[0] = new ByteArrayOutputStream(); + sinks[1] = new ByteArrayOutputStream(); + coc.dumpStdout(new PrintStream(sinks[0], false, charset)) + .dumpStderr(new PrintStream(sinks[1], false, charset)); + } else { + sinks = new ByteArrayOutputStream[1]; + sinks[0] = new ByteArrayOutputStream(); + var ps = new PrintStream(sinks[0], false, charset); + // Redirect stderr in stdout. + coc.dumpStdout(ps).dumpStderr(ps); } } - } - static Executor of(String... cmdline) { - return new Executor().setCommandLine(cmdline); - } - - static Executor of(ProcessBuilder pb) { - return new Executor().setProcessBuilder(pb); - } - - private static String createLogMessage(ProcessBuilder pb, boolean quiet) { - StringBuilder sb = new StringBuilder(); - sb.append((quiet) ? pb.command().get(0) : pb.command()); - if (pb.directory() != null) { - sb.append(String.format(" in %s", pb.directory().getAbsolutePath())); + String create() { + if (isBinaryOutput()) { + // In case of binary output: + // - Convert binary stdout to text using ISO-8859-1 encoding and + // replace non-printable characters with the question mark symbol (?). + // - Convert binary stderr to text using designated encoding (assume stderr is always a character stream). + // - Merge text stdout and stderr into a single string; + // stderr first, stdout follows, with the aim to present user error messages first. + var sb = new StringBuilder(); + var stdout = sinks[0].toString(StandardCharsets.ISO_8859_1).replaceAll("[^\\p{Print}\\p{Space}]", "?"); + return sb.append(sinks[1].toString(charset)).append(stdout).toString(); + } else { + return sinks[0].toString(charset); + } } - return sb.toString(); + + private boolean isBinaryOutput() { + return sinks.length == 2; + } + + private final ByteArrayOutputStream sinks[]; + private final Charset charset; } - public static final int INFINITE_TIMEOUT = -1; - - private ProcessBuilder pb; - private boolean saveOutput; - private boolean writeOutputToFile; + private final CommandOutputControl commandOutputControl; private boolean quietCommand; - private long timeout = INFINITE_TIMEOUT; - private List output; - private Consumer> outputConsumer; + private final List args; + private ProcessBuilder processBuilder; + private ToolProvider toolProvider; + private Duration timeout; + private UnaryOperator mapper; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java new file mode 100644 index 00000000000..ce703358b82 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java @@ -0,0 +1,33 @@ +/* + * 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.jpackage.internal; + +@FunctionalInterface +interface ExecutorFactory { + + Executor executor(); + + static final ExecutorFactory DEFAULT = Executor::new; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java new file mode 100644 index 00000000000..91ae37870a5 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java @@ -0,0 +1,85 @@ +/* + * 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.jpackage.internal; + +import java.io.PrintWriter; +import java.util.Optional; +import java.util.function.Supplier; + +public final class Globals { + + private Globals() { + } + + Globals objectFactory(ObjectFactory v) { + checkMutable(); + objectFactory = Optional.ofNullable(v).orElse(ObjectFactory.DEFAULT); + return this; + } + + ObjectFactory objectFactory() { + return objectFactory; + } + + Globals executorFactory(ExecutorFactory v) { + return objectFactory(ObjectFactory.build(objectFactory).executorFactory(v).create()); + } + + Log.Logger logger() { + return logger; + } + + public void loggerOutputStreams(PrintWriter out, PrintWriter err) { + logger.setPrintWriter(out, err); + } + + public void loggerVerbose() { + logger.setVerbose(); + } + + public static int main(Supplier mainBody) { + if (INSTANCE.isBound()) { + return mainBody.get(); + } else { + return ScopedValue.where(INSTANCE, new Globals()).call(mainBody::get); + } + } + + public static Globals instance() { + return INSTANCE.orElse(DEFAULT); + } + + private void checkMutable() { + if (this == DEFAULT) { + throw new UnsupportedOperationException("Can't modify immutable instance"); + } + } + + private ObjectFactory objectFactory = ObjectFactory.DEFAULT; + private final Log.Logger logger = new Log.Logger(); + + private static final ScopedValue INSTANCE = ScopedValue.newInstance(); + private static final Globals DEFAULT = new Globals(); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java index aac113d7777..08cf0c10982 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.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 @@ -26,12 +26,9 @@ package jdk.jpackage.internal; import java.io.IOException; -import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; import jdk.jpackage.internal.model.JPackageException; /** @@ -50,46 +47,6 @@ final class IOUtils { StandardCopyOption.COPY_ATTRIBUTES); } - public static void exec(ProcessBuilder pb) - throws IOException { - exec(pb, false, null, false, Executor.INFINITE_TIMEOUT); - } - - // timeout in seconds. -1 will be return if process timeouts. - public static void exec(ProcessBuilder pb, long timeout) - throws IOException { - exec(pb, false, null, false, timeout); - } - - static void exec(ProcessBuilder pb, boolean testForPresenceOnly, - PrintStream consumer, boolean writeOutputToFile, long timeout) - throws IOException { - exec(pb, testForPresenceOnly, consumer, writeOutputToFile, - timeout, false); - } - - static void exec(ProcessBuilder pb, boolean testForPresenceOnly, - PrintStream consumer, boolean writeOutputToFile, - long timeout, boolean quiet) throws IOException { - List output = new ArrayList<>(); - Executor exec = Executor.of(pb) - .setWriteOutputToFile(writeOutputToFile) - .setTimeout(timeout) - .setQuiet(quiet) - .setOutputConsumer(lines -> { - lines.forEach(output::add); - if (consumer != null) { - output.forEach(consumer::println); - } - }); - - if (testForPresenceOnly) { - exec.execute(); - } else { - exec.executeExpectSuccess(); - } - } - static void writableOutputDir(Path outdir) { if (!Files.isDirectory(outdir)) { try { @@ -103,15 +60,4 @@ final class IOUtils { throw new JPackageException(I18N.format("error.cannot-write-to-output-dir", outdir.toAbsolutePath())); } } - - public static long getPID(Process p) { - try { - return p.pid(); - } catch (UnsupportedOperationException ex) { - Log.verbose(ex); // Just log exception and ignore it. This method - // is used for verbose output, so not a problem - // if unsupported. - return -1; - } - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index 37f166a4e7c..f960f8dc2bf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.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 @@ -22,13 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.jpackage.internal; + import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; import java.io.File; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; @@ -50,7 +51,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.module.ModulePath; import jdk.jpackage.internal.model.AppImageLayout; -import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.RuntimeBuilder; @@ -58,27 +58,15 @@ import jdk.jpackage.internal.model.RuntimeBuilder; final class JLinkRuntimeBuilder implements RuntimeBuilder { private JLinkRuntimeBuilder(List jlinkCmdLine) { - this.jlinkCmdLine = jlinkCmdLine; + this.jlinkCmdLine = Objects.requireNonNull(jlinkCmdLine); } @Override public void create(AppImageLayout appImageLayout) { - var args = new ArrayList(); - args.add("--output"); - args.add(appImageLayout.runtimeDirectory().toString()); - args.addAll(jlinkCmdLine); - - StringWriter writer = new StringWriter(); - PrintWriter pw = new PrintWriter(writer); - - int retVal = LazyLoad.JLINK_TOOL.run(pw, pw, args.toArray(String[]::new)); - String jlinkOut = writer.toString(); - - args.add(0, "jlink"); - Log.verbose(args, List.of(jlinkOut), retVal, -1); - if (retVal != 0) { - throw new JPackageException(I18N.format("error.jlink.failed", jlinkOut)); - } + toRunnable(Executor.of() + .toolProvider(LazyLoad.JLINK_TOOL) + .args("--output", appImageLayout.runtimeDirectory().toString()) + .args(jlinkCmdLine)::executeExpectSuccess).run(); } @Override diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java index b14b4ab22ca..5c27ef67500 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, 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 @@ -28,7 +28,6 @@ package jdk.jpackage.internal; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; /** * Log @@ -62,16 +61,6 @@ public class Log { this.err = err; } - public void flush() { - if (out != null) { - out.flush(); - } - - if (err != null) { - err.flush(); - } - } - public void info(String msg) { if (out != null) { out.println(msg); @@ -105,29 +94,6 @@ public class Log { } } - public void verbose(List strings, - List output, int returnCode, long pid) { - if (verbose) { - StringBuilder sb = new StringBuilder(); - sb.append("Command [PID: "); - sb.append(pid); - sb.append("]:\n "); - - for (String s : strings) { - sb.append(" " + s); - } - verbose(sb.toString()); - if (output != null && !output.isEmpty()) { - sb = new StringBuilder("Output:"); - for (String s : output) { - sb.append("\n " + s); - } - verbose(sb.toString()); - } - verbose("Returned: " + returnCode + "\n"); - } - } - private String addTimestamp(String msg) { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); Date time = new Date(System.currentTimeMillis()); @@ -135,51 +101,27 @@ public class Log { } } - private static final InheritableThreadLocal instance = - new InheritableThreadLocal() { - @Override protected Logger initialValue() { - return new Logger(); - } - }; - - public static void setPrintWriter (PrintWriter out, PrintWriter err) { - instance.get().setPrintWriter(out, err); - } - - public static void flush() { - instance.get().flush(); - } - public static void info(String msg) { - instance.get().info(msg); + Globals.instance().logger().info(msg); } public static void fatalError(String msg) { - instance.get().fatalError(msg); + Globals.instance().logger().fatalError(msg); } public static void error(String msg) { - instance.get().error(msg); - } - - public static void setVerbose() { - instance.get().setVerbose(); + Globals.instance().logger().error(msg); } public static boolean isVerbose() { - return instance.get().isVerbose(); + return Globals.instance().logger().isVerbose(); } public static void verbose(String msg) { - instance.get().verbose(msg); + Globals.instance().logger().verbose(msg); } public static void verbose(Throwable t) { - instance.get().verbose(t); - } - - public static void verbose(List strings, List out, - int ret, long pid) { - instance.get().verbose(strings, out, ret, pid); + Globals.instance().logger().verbose(t); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java new file mode 100644 index 00000000000..f1a83eb9eab --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.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. 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.jpackage.internal; + +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.util.CompositeProxy; + +interface ObjectFactory extends ExecutorFactory, RetryExecutorFactory { + + static ObjectFactory.Builder build() { + return new Builder(); + } + + static ObjectFactory.Builder build(ObjectFactory from) { + return build().initFrom(from); + } + + static final class Builder { + private Builder() { + } + + ObjectFactory create() { + return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create( + ObjectFactory.class, + Optional.ofNullable(executorFactory).orElse(ExecutorFactory.DEFAULT), + Optional.ofNullable(retryExecutorFactory).orElse(RetryExecutorFactory.DEFAULT)); + } + + Builder initFrom(ObjectFactory of) { + Objects.requireNonNull(of); + return executorFactory(of).retryExecutorFactory(of); + } + + Builder executorFactory(ExecutorFactory v) { + executorFactory = v; + return this; + } + + Builder retryExecutorFactory(RetryExecutorFactory v) { + retryExecutorFactory = v; + return this; + } + + private ExecutorFactory executorFactory; + private RetryExecutorFactory retryExecutorFactory; + } + + static final ObjectFactory DEFAULT = build().create(); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index c94dada9262..f15768b2cbf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.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 @@ -29,6 +29,7 @@ import static java.util.stream.Collectors.toMap; import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.util.ArrayList; @@ -39,12 +40,16 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Stream; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.pipeline.DirectedEdge; import jdk.jpackage.internal.pipeline.FixedDAG; @@ -97,7 +102,7 @@ final class PackagingPipeline { /** * The way to access packaging build environment before building a package in a pipeline. */ - interface StartupParameters { + sealed interface StartupParameters { BuildEnv packagingEnv(); } @@ -127,6 +132,7 @@ final class PackagingPipeline { enum PackageTaskID implements TaskID { RUN_POST_IMAGE_USER_SCRIPT, CREATE_CONFIG_FILES, + DELETE_OLD_PACKAGE_FILE, CREATE_PACKAGE_FILE } @@ -183,9 +189,11 @@ final class PackagingPipeline { void execute() throws IOException; } - record TaskConfig(Optional action) { + record TaskConfig(Optional action, Optional beforeAction, Optional afterAction) { TaskConfig { Objects.requireNonNull(action); + Objects.requireNonNull(beforeAction); + Objects.requireNonNull(afterAction); } } @@ -196,47 +204,64 @@ final class PackagingPipeline { final class TaskBuilder extends TaskSpecBuilder { - private TaskBuilder(TaskID id) { - super(id); - } - - private TaskBuilder(TaskID id, TaskConfig config) { - this(id); - config.action().ifPresent(this::setAction); - } - - private TaskBuilder setAction(TaskAction v) { - action = v; - return this; - } - TaskBuilder noaction() { - action = null; - return this; + return setAction(ActionRole.WORKLOAD, null); } TaskBuilder applicationAction(ApplicationImageTaskAction action) { - return setAction(action); + return applicationAction(ActionRole.WORKLOAD, action); } TaskBuilder appImageAction(AppImageTaskAction action) { - return setAction(action); + return appImageAction(ActionRole.WORKLOAD, action); } TaskBuilder copyAction(CopyAppImageTaskAction action) { - return setAction(action); + return copyAction(ActionRole.WORKLOAD, action); } TaskBuilder packageAction(PackageTaskAction action) { - return setAction(action); + return packageAction(ActionRole.WORKLOAD, action); } TaskBuilder action(NoArgTaskAction action) { - return setAction(action); + return action(ActionRole.WORKLOAD, action); + } + + TaskBuilder logAppImageActionBegin(String keyId, Function, Object[]> formatArgsSupplier) { + return logAppImageAction(ActionRole.BEFORE, keyId, formatArgsSupplier); + } + + TaskBuilder logAppImageActionEnd(String keyId, Function, Object[]> formatArgsSupplier) { + return logAppImageAction(ActionRole.AFTER, keyId, formatArgsSupplier); + } + + TaskBuilder logPackageActionBegin(String keyId, Function, Object[]> argsSupplier) { + return logPackageAction(ActionRole.BEFORE, keyId, argsSupplier); + } + + TaskBuilder logPackageActionEnd(String keyId, Function, Object[]> argsSupplier) { + return logPackageAction(ActionRole.AFTER, keyId, argsSupplier); + } + + TaskBuilder logActionBegin(String keyId, Supplier formatArgsSupplier) { + return logAction(ActionRole.BEFORE, keyId, formatArgsSupplier); + } + + TaskBuilder logActionBegin(String keyId, Object... formatArgsSupplier) { + return logAction(ActionRole.BEFORE, keyId, () -> formatArgsSupplier); + } + + TaskBuilder logActionEnd(String keyId, Supplier formatArgsSupplier) { + return logAction(ActionRole.AFTER, keyId, formatArgsSupplier); + } + + TaskBuilder logActionEnd(String keyId, Object... formatArgsSupplier) { + return logAction(ActionRole.AFTER, keyId, () -> formatArgsSupplier); } boolean hasAction() { - return action != null; + return workloadAction != null; } @Override @@ -272,13 +297,109 @@ final class PackagingPipeline { } Builder add() { - final var config = new TaskConfig(Optional.ofNullable(action)); + final var config = new TaskConfig( + Optional.ofNullable(workloadAction), + Optional.ofNullable(beforeAction), + Optional.ofNullable(afterAction)); taskConfig.put(task(), config); createLinks().forEach(Builder.this::linkTasks); return Builder.this; } - private TaskAction action; + + private enum ActionRole { + WORKLOAD(TaskBuilder::setWorkloadAction), + BEFORE(TaskBuilder::setBeforeAction), + AFTER(TaskBuilder::setAfterAction), + ; + + ActionRole(BiConsumer actionSetter) { + this.actionSetter = Objects.requireNonNull(actionSetter); + } + + TaskBuilder setAction(TaskBuilder taskBuilder, TaskAction action) { + actionSetter.accept(taskBuilder, action); + return taskBuilder; + } + + private final BiConsumer actionSetter; + } + + + private TaskBuilder(TaskID id) { + super(id); + } + + private TaskBuilder(TaskID id, TaskConfig config) { + this(id); + config.action().ifPresent(this::setWorkloadAction); + config.beforeAction().ifPresent(this::setBeforeAction); + config.afterAction().ifPresent(this::setAfterAction); + } + + private TaskBuilder setAction(ActionRole role, TaskAction v) { + return role.setAction(this, v); + } + + private TaskBuilder setWorkloadAction(TaskAction v) { + workloadAction = v; + return this; + } + + private TaskBuilder setBeforeAction(TaskAction v) { + beforeAction = v; + return this; + } + + private TaskBuilder setAfterAction(TaskAction v) { + afterAction = v; + return this; + } + + private TaskBuilder applicationAction(ActionRole role, ApplicationImageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder appImageAction(ActionRole role, AppImageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder copyAction(ActionRole role, CopyAppImageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder packageAction(ActionRole role, PackageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder action(ActionRole role, NoArgTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder logAppImageAction(ActionRole role, String keyId, Function, Object[]> formatArgsSupplier) { + Objects.requireNonNull(keyId); + return appImageAction(role, (AppImageBuildEnv env) -> { + Log.verbose(I18N.format(keyId, formatArgsSupplier.apply(env))); + }); + } + + private TaskBuilder logPackageAction(ActionRole role, String keyId, Function, Object[]> formatArgsSupplier) { + Objects.requireNonNull(keyId); + return packageAction(role, (PackageBuildEnv env) -> { + Log.verbose(I18N.format(keyId, formatArgsSupplier.apply(env))); + }); + } + + private TaskBuilder logAction(ActionRole role, String keyId, Supplier formatArgsSupplier) { + Objects.requireNonNull(keyId); + return action(role, () -> { + Log.verbose(I18N.format(keyId, formatArgsSupplier.get())); + }); + } + + private TaskAction workloadAction; + private TaskAction beforeAction; + private TaskAction afterAction; } Builder linkTasks(DirectedEdge edge) { @@ -294,7 +415,11 @@ final class PackagingPipeline { } TaskBuilder task(TaskID id) { - return new TaskBuilder(id); + return Optional.ofNullable(taskConfig.get(id)).map(taskConfig -> { + return new TaskBuilder(id, taskConfig); + }).orElseGet(() -> { + return new TaskBuilder(id); + }); } Stream configuredTasks() { @@ -392,6 +517,8 @@ final class PackagingPipeline { builder.task(PackageTaskID.CREATE_PACKAGE_FILE) .addDependent(PrimaryTaskID.PACKAGE) + .logActionBegin("message.create-package") + .logActionEnd("message.package-created") .add(); builder.task(PrimaryTaskID.PACKAGE).add(); @@ -425,6 +552,17 @@ final class PackagingPipeline { .run(env.env(), env.pkg().app().name()); } + static void deleteOutputBundle(PackageBuildEnv env) throws IOException { + + var outputBundle = env.outputDir().resolve(env.pkg().packageFileNameWithSuffix()); + + try { + Files.deleteIfExists(outputBundle); + } catch (IOException ex) { + throw new JPackageException(I18N.format("error.output-bundle-cannot-be-overwritten", outputBundle.toAbsolutePath()), ex); + } + } + private PackagingPipeline(FixedDAG taskGraph, Map taskConfig, UnaryOperator contextMapper) { this.taskGraph = Objects.requireNonNull(taskGraph); @@ -645,7 +783,13 @@ final class PackagingPipeline { } if (accepted) { + if (config.beforeAction.isPresent()) { + context.execute(config.beforeAction.orElseThrow()); + } context.execute(config.action.orElseThrow()); + if (config.afterAction.isPresent()) { + context.execute(config.afterAction.orElseThrow()); + } } return null; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java deleted file mode 100644 index f806217e8f9..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. - * 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.jpackage.internal; - -import java.io.IOException; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public final class RetryExecutor { - public RetryExecutor() { - setMaxAttemptsCount(5); - setAttemptTimeoutMillis(2 * 1000); - setWriteOutputToFile(false); - } - - public RetryExecutor setMaxAttemptsCount(int v) { - attempts = v; - return this; - } - - public RetryExecutor setAttemptTimeoutMillis(int v) { - timeoutMillis = v; - return this; - } - - public RetryExecutor saveOutput(boolean v) { - saveOutput = v; - return this; - } - - public List getOutput() { - return output; - } - - public RetryExecutor setWriteOutputToFile(boolean v) { - writeOutputToFile = v; - return this; - } - - public RetryExecutor setExecutorInitializer(Consumer v) { - executorInitializer = v; - return this; - } - - public void abort() { - aborted = true; - } - - public boolean isAborted() { - return aborted; - } - - static RetryExecutor retryOnKnownErrorMessage(String v) { - RetryExecutor result = new RetryExecutor(); - return result.setExecutorInitializer(exec -> { - exec.setOutputConsumer(output -> { - if (!output.anyMatch(v::equals)) { - result.abort(); - } - }); - }); - } - - public void execute(String cmdline[]) throws IOException { - executeLoop(() -> - Executor.of(cmdline).setWriteOutputToFile(writeOutputToFile)); - } - - public void execute(ProcessBuilder pb) throws IOException { - executeLoop(() -> - Executor.of(pb).setWriteOutputToFile(writeOutputToFile)); - } - - private void executeLoop(Supplier execSupplier) throws IOException { - aborted = false; - for (;;) { - if (aborted) { - break; - } - - try { - Executor exec = execSupplier.get().saveOutput(saveOutput); - if (executorInitializer != null) { - executorInitializer.accept(exec); - } - exec.executeExpectSuccess(); - if (saveOutput) { - output = exec.getOutput(); - } - break; - } catch (IOException ex) { - if (aborted || (--attempts) <= 0) { - throw ex; - } - } - - try { - Thread.sleep(timeoutMillis); - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } - } - } - - private Consumer executorInitializer; - private boolean aborted; - private int attempts; - private int timeoutMillis; - private boolean saveOutput; - private List output; - private boolean writeOutputToFile; -} diff --git a/src/java.desktop/share/native/libjsound/Platform.c b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java similarity index 69% rename from src/java.desktop/share/native/libjsound/Platform.c rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java index ecb389c89b7..3efb522abd4 100644 --- a/src/java.desktop/share/native/libjsound/Platform.c +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, 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 @@ -22,22 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package jdk.jpackage.internal; +import jdk.jpackage.internal.util.RetryExecutor; -#include "Utilities.h" -// Platform.java includes -#include "com_sun_media_sound_Platform.h" +@FunctionalInterface +interface RetryExecutorFactory { -/* - * Declare library specific JNI_Onload entry if static build - */ -DEF_STATIC_JNI_OnLoad + RetryExecutor retryExecutor(Class exceptionType); -/* - * Class: com_sun_media_sound_Platform - * Method: nIsBigEndian - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_com_sun_media_sound_Platform_nIsBigEndian(JNIEnv *env, jclass clss) { - return UTIL_IsBigEndianPlatform(); + static final RetryExecutorFactory DEFAULT = RetryExecutor::new; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.java index de98e97c922..5d9a1d6d147 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.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,5 +24,5 @@ */ package jdk.jpackage.internal; -public interface SystemEnvironment { +interface SystemEnvironment { } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java index 13e87d5cfa6..9440aff3a53 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,32 +121,32 @@ final class ToolValidator { cmdline.addAll(args); } - boolean canUseTool[] = new boolean[1]; + boolean canUseTool = false; if (minimalVersion == null) { // No version check. - canUseTool[0] = true; + canUseTool = true; } - String[] version = new String[1]; + String version = null; try { - Executor.of(cmdline.toArray(String[]::new)).setQuiet(true).setOutputConsumer(lines -> { - if (versionParser != null && minimalVersion != null) { - version[0] = versionParser.apply(lines); - if (version[0] != null && minimalVersion.compareTo(version[0]) <= 0) { - canUseTool[0] = true; - } + var result = Executor.of(cmdline).setQuiet(true).saveOutput().execute(); + var lines = result.content(); + if (versionParser != null && minimalVersion != null) { + version = versionParser.apply(lines.stream()); + if (version != null && minimalVersion.compareTo(version) <= 0) { + canUseTool = true; } - }).execute(); + } } catch (IOException e) { return new ConfigException(I18N.format("error.tool-error", toolPath, e.getMessage()), null, e); } - if (canUseTool[0]) { + if (canUseTool) { // All good. Tool can be used. return null; } else if (toolOldVersionErrorHandler != null) { - return toolOldVersionErrorHandler.apply(toolPath, version[0]); + return toolOldVersionErrorHandler.apply(toolPath, version); } else { return new ConfigException( I18N.format("error.tool-old-version", toolPath, minimalVersion), diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index 9feb5b40944..562a0d2d3c1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.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 @@ -29,10 +29,12 @@ import static jdk.jpackage.internal.cli.StandardOption.HELP; import static jdk.jpackage.internal.cli.StandardOption.VERBOSE; import static jdk.jpackage.internal.cli.StandardOption.VERSION; +import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.StringReader; import java.io.UncheckedIOException; import java.nio.file.NoSuchFileException; import java.util.Collection; @@ -45,9 +47,14 @@ import java.util.function.Supplier; import java.util.spi.ToolProvider; import jdk.internal.opt.CommandLine; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.Globals; import jdk.jpackage.internal.Log; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.model.SelfContainedException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; import jdk.jpackage.internal.util.Slot; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -56,7 +63,15 @@ import jdk.jpackage.internal.util.function.ExceptionBox; */ public final class Main { - public static final class Provider implements ToolProvider { + public record Provider(Supplier bundlingEnvSupplier) implements ToolProvider { + + public Provider { + Objects.requireNonNull(bundlingEnvSupplier); + } + + public Provider() { + this(DefaultBundlingEnvironmentLoader.INSTANCE); + } @Override public String name() { @@ -65,13 +80,13 @@ public final class Main { @Override public int run(PrintWriter out, PrintWriter err, String... args) { - return Main.run(out, err, args); + return Main.run(bundlingEnvSupplier, out, err, args); } @Override public int run(PrintStream out, PrintStream err, String... args) { - PrintWriter outWriter = new PrintWriter(out, true); - PrintWriter errWriter = new PrintWriter(err, true); + PrintWriter outWriter = toPrintWriter(out); + PrintWriter errWriter = toPrintWriter(err); try { try { return run(outWriter, errWriter, args); @@ -89,12 +104,28 @@ public final class Main { } public static void main(String... args) { - var out = new PrintWriter(System.out, true); - var err = new PrintWriter(System.err, true); + var out = toPrintWriter(System.out); + var err = toPrintWriter(System.err); System.exit(run(out, err, args)); } - public static int run(PrintWriter out, PrintWriter err, String... args) { + static int run(PrintWriter out, PrintWriter err, String... args) { + return run(DefaultBundlingEnvironmentLoader.INSTANCE, out, err, args); + } + + static int run(Supplier bundlingEnvSupplier, PrintWriter out, PrintWriter err, String... args) { + return Globals.main(() -> { + return runWithGlobals(bundlingEnvSupplier, out, err, args); + }); + } + + private static int runWithGlobals( + Supplier bundlingEnvSupplier, + PrintWriter out, + PrintWriter err, + String... args) { + + Objects.requireNonNull(bundlingEnvSupplier); Objects.requireNonNull(args); for (String arg : args) { Objects.requireNonNull(arg); @@ -102,7 +133,7 @@ public final class Main { Objects.requireNonNull(out); Objects.requireNonNull(err); - Log.setPrintWriter(out, err); + Globals.instance().loggerOutputStreams(out, err); final var runner = new Runner(t -> { new ErrorReporter(_ -> { @@ -128,8 +159,7 @@ public final class Main { return preprocessStatus; } - final var bundlingEnv = ServiceLoader.load(CliBundlingEnvironment.class, - CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow(); + final var bundlingEnv = bundlingEnvSupplier.get(); final var parseResult = Utils.buildParser(OperatingSystem.current(), bundlingEnv).create().apply(mappedArgs.get()); @@ -155,7 +185,7 @@ public final class Main { } if (VERBOSE.containsIn(options)) { - Log.setVerbose(); + Globals.instance().loggerVerbose(); } final var optionsProcessor = new OptionsProcessor(parsedOptionsBuilder, bundlingEnv); @@ -215,44 +245,60 @@ public final class Main { reportError(ex.getCause()); } else if (t instanceof UncheckedIOException ex) { reportError(ex.getCause()); + } else if (t instanceof UnexpectedResultException ex) { + printExternalCommandError(ex); } else { printError(t, Optional.empty()); } } - private void printError(Throwable t, Optional advice) { - var isAlienException = isAlienExceptionType(t); + private void printExternalCommandError(UnexpectedResultException ex) { + var result = ex.getResult(); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + var printableCommandLine = result.execAttrs().printableCommandLine(); - if (isAlienException || verbose) { + if (verbose) { + stackTracePrinter.accept(ex); + } + + String msg; + if (ex instanceof UnexpectedExitCodeException) { + msg = I18N.format("error.command-failed-unexpected-exit-code", result.getExitCode(), printableCommandLine); + } else if (result.exitCode().isPresent()) { + msg = I18N.format("error.command-failed-unexpected-output", printableCommandLine); + } else { + msg = I18N.format("error.command-failed-timed-out", printableCommandLine); + } + + messagePrinter.accept(I18N.format("message.error-header", msg)); + if (!verbose) { + messagePrinter.accept(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(messagePrinter); + } + } + } + + private void printError(Throwable t, Optional advice) { + var isSelfContained = isSelfContained(t); + + if (!isSelfContained || verbose) { stackTracePrinter.accept(t); } String msg; - if (isAlienException) { - msg = t.toString(); - } else { + if (isSelfContained) { msg = t.getMessage(); + } else { + msg = t.toString(); } messagePrinter.accept(I18N.format("message.error-header", msg)); advice.ifPresent(v -> messagePrinter.accept(I18N.format("message.advice-header", v))); } - private static boolean isAlienExceptionType(Throwable t) { - switch (t) { - case JPackageException _ -> { - return false; - } - case Utils.ParseException _ -> { - return false; - } - case StandardOption.AddLauncherIllegalArgumentException _ -> { - return false; - } - default -> { - return true; - } - } + private static boolean isSelfContained(Throwable t) { + return t.getClass().getAnnotation(SelfContainedException.class) != null; } } @@ -285,4 +331,19 @@ public final class Main { private static String getVersion() { return System.getProperty("java.version"); } + + private static PrintWriter toPrintWriter(PrintStream ps) { + return new PrintWriter(ps, true, ps.charset()); + } + + private enum DefaultBundlingEnvironmentLoader implements Supplier { + INSTANCE; + + @Override + public CliBundlingEnvironment get() { + return ServiceLoader.load( + CliBundlingEnvironment.class, + CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow(); + } + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java index e27d6472369..6cd1c05b57e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.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 @@ -73,6 +73,7 @@ final class OptionSpecBuilder { valuePattern = other.valuePattern; converterBuilder = other.converterBuilder.copy(); validatorBuilder = other.validatorBuilder.copy(); + validator = other.validator; if (other.arrayDefaultValue != null) { arrayDefaultValue = Arrays.copyOf(other.arrayDefaultValue, other.arrayDefaultValue.length); @@ -135,10 +136,20 @@ final class OptionSpecBuilder { scope, OptionSpecBuilder.this.mergePolicy().orElse(MergePolicy.CONCATENATE), defaultArrayOptionalValue(), - Optional.of(arryValuePattern()), + Optional.of(arrayValuePattern()), OptionSpecBuilder.this.description().orElse("")); } + Optional> createValidator() { + return Optional.ofNullable(validator).or(() -> { + if (validatorBuilder.hasValidatingMethod()) { + return Optional.of(validatorBuilder.create()); + } else { + return Optional.empty(); + } + }); + } + OptionSpecBuilder tokenizer(String splitRegexp) { Objects.requireNonNull(splitRegexp); return tokenizer(str -> { @@ -162,11 +173,13 @@ final class OptionSpecBuilder { OptionSpecBuilder validatorExceptionFormatString(String v) { validatorBuilder.formatString(v); + validator = null; return this; } OptionSpecBuilder validatorExceptionFormatString(UnaryOperator mutator) { validatorBuilder.formatString(mutator.apply(validatorBuilder.formatString().orElse(null))); + validator = null; return this; } @@ -182,6 +195,7 @@ final class OptionSpecBuilder { OptionSpecBuilder validatorExceptionFactory(OptionValueExceptionFactory v) { validatorBuilder.exceptionFactory(v); + validator = null; return this; } @@ -225,18 +239,27 @@ final class OptionSpecBuilder { OptionSpecBuilder validator(Predicate v) { validatorBuilder.predicate(v::test); + validator = null; return this; } @SuppressWarnings("overloads") OptionSpecBuilder validator(Consumer v) { validatorBuilder.consumer(v::accept); + validator = null; return this; } @SuppressWarnings("overloads") OptionSpecBuilder validator(UnaryOperator> mutator) { validatorBuilder = mutator.apply(validatorBuilder); + validator = null; + return this; + } + + OptionSpecBuilder validator(Validator v) { + validatorBuilder.predicate(null).consumer(null); + validator = Objects.requireNonNull(v); return this; } @@ -247,6 +270,7 @@ final class OptionSpecBuilder { OptionSpecBuilder withoutValidator() { validatorBuilder.predicate(null).consumer(null); + validator = null; return this; } @@ -423,14 +447,6 @@ final class OptionSpecBuilder { } } - private Optional> createValidator() { - if (validatorBuilder.hasValidatingMethod()) { - return Optional.of(validatorBuilder.create()); - } else { - return Optional.empty(); - } - } - private OptionValueConverter createArrayConverter() { final var newBuilder = converterBuilder.copy(); newBuilder.tokenizer(Optional.ofNullable(arrayTokenizer).orElse(str -> { @@ -440,7 +456,7 @@ final class OptionSpecBuilder { return newBuilder.createArray(); } - private String arryValuePattern() { + private String arrayValuePattern() { final var elementValuePattern = OptionSpecBuilder.this.valuePattern().orElseThrow(); if (arrayValuePatternSeparator == null) { return elementValuePattern; @@ -468,6 +484,7 @@ final class OptionSpecBuilder { private String valuePattern; private OptionValueConverter.Builder converterBuilder = OptionValueConverter.build(); private Validator.Builder validatorBuilder = Validator.build(); + private Validator validator; private T[] arrayDefaultValue; private String arrayValuePatternSeparator; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java index 67e87d55d8b..363aa1b863e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.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 @@ -56,7 +56,7 @@ import jdk.jpackage.internal.model.BundlingEnvironment; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.JPackageException; -import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.BundleType; /** * Analyzes jpackage command line structure. @@ -250,7 +250,7 @@ final class OptionsAnalyzer { return error("ERR_NoInstallerEntryPoint", mapFormatArguments(optionSpec)); } else { return error("ERR_InvalidTypeOption", mapFormatArguments( - optionSpec, bundlingOperation.packageTypeValue())); + optionSpec, bundlingOperation.bundleTypeValue())); } } @@ -267,30 +267,31 @@ final class OptionsAnalyzer { final var typeOption = TYPE.getOption(); return cmdline.find(typeOption).map(obj -> { - if (obj instanceof PackageType packageType) { - return packageType; + if (obj instanceof BundleType bundleType) { + return bundleType; } else { - return typeOption.spec() + var spec = new StandardOptionContext(os).mapOptionSpec(typeOption.spec()); + return spec .converter().orElseThrow() - .convert(typeOption.spec().name(), StringToken.of(((String[])obj)[0])) + .convert(spec.name(), StringToken.of(((String[])obj)[0])) .orElseThrow(); } - }).map(packageType -> { - // Find standard bundling operations producing the given package type. + }).map(bundleType -> { + // Find standard bundling operations producing the given bundle type. var bundlingOperations = Stream.of(StandardBundlingOperation.values()).filter(op -> { - return op.packageType().equals(packageType); + return op.bundleType().equals(bundleType); }).toList(); if (bundlingOperations.isEmpty()) { // jpackage internal error: none of the standard bundling operations produce - // bundles of the `packageType`. + // bundles of the `bundleType`. throw new AssertionError(String.format( "None of the standard bundling operations produce bundles of type [%s]", - packageType)); + bundleType)); } else if (bundlingOperations.size() == 1) { return bundlingOperations.getFirst(); } else { - // Multiple standard bundling operations produce the `packageType` package type. + // Multiple standard bundling operations produce the `bundleType` bundle type. // Filter those that belong to the current OS bundlingOperations = bundlingOperations.stream().filter(op -> { return op.os().equals(OperatingSystem.current()); @@ -298,10 +299,10 @@ final class OptionsAnalyzer { if (bundlingOperations.isEmpty()) { // jpackage internal error: none of the standard bundling operations produce - // bundles of the `packageType` on the current OS. + // bundles of the `bundleType` on the current OS. throw new AssertionError(String.format( "None of the standard bundling operations produce bundles of type [%s] on %s", - packageType, OperatingSystem.current())); + bundleType, OperatingSystem.current())); } else if (bundlingOperations.size() == 1) { return bundlingOperations.getFirst(); } else if (StandardBundlingOperation.MACOS_APP_IMAGE.containsAll(bundlingOperations)) { @@ -316,7 +317,7 @@ final class OptionsAnalyzer { } } }).orElseGet(() -> { - // No package type specified, use the default bundling operation in the given environment. + // No bundle type specified, use the default bundling operation in the given environment. return env.defaultOperation().map(descriptor -> { return Stream.of(StandardBundlingOperation.values()).filter(op -> { return descriptor.equals(op.descriptor()); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java index 330a650e513..42f90536753 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.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 @@ -169,15 +169,6 @@ public final class StandardAppImageFileOption { .mutate(setPlatformScope(OperatingSystem.MACOS)) .toOptionValueBuilder().id(StandardOption.MAC_APP_STORE.id()).create(); - /** - * Is an application image is signed. macOS-only. - */ - public static final OptionValue MAC_SIGNED = booleanOption("signed") - .inScope(AppImageFileOptionScope.APP) - .mutate(setPlatformScope(OperatingSystem.MACOS)) - .toOptionValueBuilder().id(StandardOption.MAC_SIGN.id()).create(); - - public static final class InvalidOptionValueException extends RuntimeException { InvalidOptionValueException(String str, Throwable t) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.java index 45f9194db0b..f6fcd68a6d8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.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,8 +24,6 @@ */ package jdk.jpackage.internal.cli; -import static jdk.jpackage.internal.model.AppImagePackageType.APP_IMAGE; - import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -34,6 +32,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.model.AppImageBundleType; +import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.StandardPackageType; @@ -44,16 +44,16 @@ import jdk.jpackage.internal.util.SetBuilder; * Standard jpackage operations. */ public enum StandardBundlingOperation implements BundlingOperationOptionScope { - CREATE_WIN_APP_IMAGE(APP_IMAGE, "^(?!(linux-|mac-|win-exe-|win-msi-))", OperatingSystem.WINDOWS), - CREATE_LINUX_APP_IMAGE(APP_IMAGE, "^(?!(win-|mac-|linux-rpm-|linux-deb-))", OperatingSystem.LINUX), - CREATE_MAC_APP_IMAGE(APP_IMAGE, "^(?!(linux-|win-|mac-dmg-|mac-pkg-))", OperatingSystem.MACOS), + CREATE_WIN_APP_IMAGE(AppImageBundleType.WIN_APP_IMAGE, "^(?!(linux-|mac-|win-exe-|win-msi-))", OperatingSystem.WINDOWS), + CREATE_LINUX_APP_IMAGE(AppImageBundleType.LINUX_APP_IMAGE, "^(?!(win-|mac-|linux-rpm-|linux-deb-))", OperatingSystem.LINUX), + CREATE_MAC_APP_IMAGE(AppImageBundleType.MAC_APP_IMAGE, "^(?!(linux-|win-|mac-dmg-|mac-pkg-))", OperatingSystem.MACOS), CREATE_WIN_EXE(StandardPackageType.WIN_EXE, "^(?!(linux-|mac-|win-msi-))", OperatingSystem.WINDOWS), CREATE_WIN_MSI(StandardPackageType.WIN_MSI, "^(?!(linux-|mac-|win-exe-))", OperatingSystem.WINDOWS), CREATE_LINUX_RPM(StandardPackageType.LINUX_RPM, "^(?!(win-|mac-|linux-deb-))", OperatingSystem.LINUX), CREATE_LINUX_DEB(StandardPackageType.LINUX_DEB, "^(?!(win-|mac-|linux-rpm-))", OperatingSystem.LINUX), CREATE_MAC_PKG(StandardPackageType.MAC_PKG, "^(?!(linux-|win-|mac-dmg-))", OperatingSystem.MACOS), CREATE_MAC_DMG(StandardPackageType.MAC_DMG, "^(?!(linux-|win-|mac-pkg-))", OperatingSystem.MACOS), - SIGN_MAC_APP_IMAGE(APP_IMAGE, OperatingSystem.MACOS, Verb.SIGN); + SIGN_MAC_APP_IMAGE(AppImageBundleType.MAC_APP_IMAGE, OperatingSystem.MACOS, Verb.SIGN); /** * Supported values of the {@link BundlingOperationDescriptor#verb()} property. @@ -78,19 +78,19 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { private final String value; } - StandardBundlingOperation(PackageType packageType, String optionNameRegexp, OperatingSystem os, Verb descriptorVerb) { - this.packageType = Objects.requireNonNull(packageType); + StandardBundlingOperation(BundleType bundleType, String optionNameRegexp, OperatingSystem os, Verb descriptorVerb) { + this.bundleType = Objects.requireNonNull(bundleType); optionNamePredicate = Pattern.compile(optionNameRegexp).asPredicate(); this.os = Objects.requireNonNull(os); this.descriptorVerb = Objects.requireNonNull(descriptorVerb); } - StandardBundlingOperation(PackageType packageType, String optionNameRegexp, OperatingSystem os) { - this(packageType, optionNameRegexp, os, Verb.CREATE); + StandardBundlingOperation(BundleType bundleType, String optionNameRegexp, OperatingSystem os) { + this(bundleType, optionNameRegexp, os, Verb.CREATE); } - StandardBundlingOperation(PackageType packageType, OperatingSystem os, Verb descriptorVerb) { - this.packageType = Objects.requireNonNull(packageType); + StandardBundlingOperation(BundleType bundleType, OperatingSystem os, Verb descriptorVerb) { + this.bundleType = Objects.requireNonNull(bundleType); optionNamePredicate = v -> false; this.os = Objects.requireNonNull(os); this.descriptorVerb = Objects.requireNonNull(descriptorVerb); @@ -100,16 +100,20 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { return os; } - public String packageTypeValue() { - if (packageType.equals(APP_IMAGE)) { + public String bundleTypeValue() { + if (bundleType instanceof AppImageBundleType) { return "app-image"; } else { - return ((StandardPackageType)packageType).suffix().substring(1); + return ((StandardPackageType)bundleType).suffix().substring(1); } } + public BundleType bundleType() { + return bundleType; + } + public PackageType packageType() { - return packageType; + return (PackageType)bundleType(); } /** @@ -122,7 +126,7 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { @Override public BundlingOperationDescriptor descriptor() { - return new BundlingOperationDescriptor(os(), packageTypeValue(), descriptorVerb.value()); + return new BundlingOperationDescriptor(os(), bundleTypeValue(), descriptorVerb.value()); } public static Optional valueOf(BundlingOperationDescriptor descriptor) { @@ -199,6 +203,6 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { private final Predicate optionNamePredicate; private final OperatingSystem os; - private final PackageType packageType; + private final BundleType bundleType; private final Verb descriptorVerb; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index ec63c1fb498..fadd9bacb1c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.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 @@ -55,11 +55,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.model.AppImageBundleType; +import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.SelfContainedException; import jdk.jpackage.internal.util.SetBuilder; /** @@ -103,18 +106,18 @@ public final class StandardOption { public static final OptionValue VERBOSE = auxilaryOption("verbose").create(); - public static final OptionValue TYPE = option("type", PackageType.class).addAliases("t") + public static final OptionValue TYPE = option("type", BundleType.class).addAliases("t") .scope(StandardBundlingOperation.values()).inScope(NOT_BUILDING_APP_IMAGE) .converterExceptionFactory(ERROR_WITH_VALUE).converterExceptionFormatString("ERR_InvalidInstallerType") .converter(str -> { - Objects.requireNonNull(str); - return Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> { - return bundlingOperation.packageTypeValue().equals(str); - }).map(StandardBundlingOperation::packageType).findFirst().orElseThrow(IllegalArgumentException::new); + return parseBundleType(str, OperatingSystem.current()); }) .description("help.option.type" + resourceKeySuffix(OperatingSystem.current())) .mutate(createOptionSpecBuilderMutator((b, context) -> { b.description("help.option.type" + resourceKeySuffix(context.os())); + b.converter(str -> { + return parseBundleType(str, context.os()); + }); })).create(); public static final OptionValue INPUT = directoryOption("input").addAliases("i") @@ -233,6 +236,12 @@ public final class StandardOption { .mutate(createOptionSpecBuilderMutator((b, context) -> { if (context.os() == OperatingSystem.MACOS) { b.description("help.option.app-image" + resourceKeySuffix(context.os())); + var directoryValidator = b.createValidator().orElseThrow(); + var macBundleValidator = b + .validatorExceptionFormatString("error.parameter-not-mac-bundle") + .validator(StandardValidator.IS_VALID_MAC_BUNDLE) + .createValidator().orElseThrow(); + b.validator(Validator.and(directoryValidator, macBundleValidator)); } })) .create(); @@ -659,6 +668,23 @@ public final class StandardOption { }).defaultArrayValue(new AdditionalLauncher[0]).createArray(); } + private static BundleType parseBundleType(String str, OperatingSystem appImageOS) { + Objects.requireNonNull(str); + Objects.requireNonNull(appImageOS); + + return Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> { + return bundlingOperation.bundleTypeValue().equals(str); + }) + .filter(bundlingOperation -> { + // Skip app image bundle type if it is from another platform. + return !(bundlingOperation.bundleType() instanceof AppImageBundleType) + || (bundlingOperation.os() == appImageOS); + }) + .map(StandardBundlingOperation::bundleType) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + private static String resourceKeySuffix(OperatingSystem os) { switch (os) { case LINUX -> { @@ -677,6 +703,7 @@ public final class StandardOption { } + @SelfContainedException static class AddLauncherIllegalArgumentException extends IllegalArgumentException { AddLauncherIllegalArgumentException(String message) { @@ -727,8 +754,15 @@ public final class StandardOption { // // regexp for parsing args (for example, for additional launchers) - private static Pattern pattern = Pattern.compile( - "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); + private static Pattern PATTERN = Pattern.compile(String.format( + "(?:(?:%s|%s)|(?:\\\\[\"'\\s]|\\S))++", + createPatternComponent('\''), + createPatternComponent('\"'))); + + private static String createPatternComponent(char quoteChar) { + var str = Character.toString(quoteChar); + return String.format("(?:%s(?:\\\\%s|[^%s])*+(?:%s|$))", str, str, str, str); + } static List getArgumentList(String inputString) { Objects.requireNonNull(inputString); @@ -741,7 +775,7 @@ public final class StandardOption { // The "pattern" regexp attempts to abide to the rule that // strings are delimited by whitespace unless surrounded by // quotes, then it is anything (including spaces) in the quotes. - Matcher m = pattern.matcher(inputString); + Matcher m = PATTERN.matcher(inputString); while (m.find()) { String s = inputString.substring(m.start(), m.end()).trim(); // Ensure we do not have an empty string. trim() will take care of diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java index 0038740f9df..cfa97439592 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.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 @@ -38,6 +38,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; import jdk.jpackage.internal.cli.Validator.ValidatingConsumerException; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.MacBundle; final public class StandardValidator { @@ -138,6 +139,10 @@ final public class StandardValidator { return true; }; + public static Predicate IS_VALID_MAC_BUNDLE = path -> { + return MacBundle.fromPath(path).isPresent(); + }; + public static final class DirectoryListingIOException extends RuntimeException { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java index 0565a8f1235..a94de355284 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.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 @@ -32,6 +32,7 @@ import java.util.function.IntPredicate; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.model.BundlingEnvironment; +import jdk.jpackage.internal.model.SelfContainedException; final class Utils { @@ -98,6 +99,7 @@ final class Utils { }); } + @SelfContainedException static final class ParseException extends RuntimeException { ParseException(String msg) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java index 0ddf0e1984f..91d9d03bd9f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.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,20 +24,55 @@ */ package jdk.jpackage.internal.cli; -import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Stream; @FunctionalInterface interface Validator { List validate(OptionName optionName, ParsedValue optionValue); - default Validator andThen(Validator after) { - return reduce(this, after); + default Validator and(Validator after) { + Objects.requireNonNull(after); + var before = this; + return (optionName, optionValue) -> { + return Stream.concat( + before.validate(optionName, optionValue).stream(), + after.validate(optionName, optionValue).stream() + ).toList(); + }; + } + + default Validator or(Validator after) { + Objects.requireNonNull(after); + var before = this; + return (optionName, optionValue) -> { + var bErrors = before.validate(optionName, optionValue); + if (bErrors.isEmpty()) { + return List.of(); + } + + var aErrors = after.validate(optionName, optionValue); + if (aErrors.isEmpty()) { + return List.of(); + } + + return Stream.concat(bErrors.stream(), aErrors.stream()).toList(); + }; + } + + @SuppressWarnings("unchecked") + static Validator and(Validator first, Validator second) { + return (Validator)first.and(second); + } + + @SuppressWarnings("unchecked") + static Validator or(Validator first, Validator second) { + return (Validator)first.or(second); } /** @@ -251,15 +286,4 @@ interface Validator { } } } - - @SafeVarargs - private static Validator reduce(Validator... validators) { - @SuppressWarnings("varargs") - var theValidators = List.of(validators); - return (optionName, optionValue) -> { - return theValidators.stream().map(validator -> { - return validator.validate(optionName, optionValue); - }).flatMap(Collection::stream).map(Exception.class::cast).toList(); - }; - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java new file mode 100644 index 00000000000..c5d2f0d1569 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java @@ -0,0 +1,51 @@ +/* + * 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 + * 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.jpackage.internal.model; + +import java.util.Objects; + +/** + * App image bundle type. + * + * @see StandardPackageType + */ +public enum AppImageBundleType implements BundleType { + + WIN_APP_IMAGE("bundle-type.win-app"), + LINUX_APP_IMAGE("bundle-type.linux-app"), + MAC_APP_IMAGE("bundle-type.mac-app"), + ; + + private AppImageBundleType(String key) { + this.key = Objects.requireNonNull(key); + } + + @Override + public String label() { + return I18N.getString(key); + } + + private final String key; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImagePackageType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/BundleType.java similarity index 77% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImagePackageType.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/BundleType.java index 4e28bb05aef..009725f3e92 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImagePackageType.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/BundleType.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 @@ -22,20 +22,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.jpackage.internal.model; -/** - * App image packaging type. - * - * @see StandardPackageType - */ -public final class AppImagePackageType implements PackageType { - private AppImagePackageType() { - } +/** + * Generic bundle type. E.g.: application image, rpm, msi are all bundle types. + */ +public sealed interface BundleType permits PackageType, AppImageBundleType { /** - * Singleton + * Returns a user-facing label of this bundle type. + * @return a user-facing label of this bundle type. */ - public static final AppImagePackageType APP_IMAGE = new AppImagePackageType(); + String label(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java new file mode 100644 index 00000000000..faed9d24d01 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.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. 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.jpackage.internal.model; + +import java.util.List; +import java.util.Objects; +import jdk.jpackage.internal.util.CommandOutputControl.ExecutableAttributes; +import jdk.jpackage.internal.util.CommandOutputControl.Result; + +/** + * {@link ExecutableAttributes} augmented with printable command output. + */ +public record ExecutableAttributesWithCapturedOutput(ExecutableAttributes execAttrs, String printableOutput) + implements ExecutableAttributes { + + public ExecutableAttributesWithCapturedOutput { + Objects.requireNonNull(execAttrs); + Objects.requireNonNull(printableOutput); + } + + @Override + public List commandLine() { + return execAttrs.commandLine(); + } + + public static Result augmentResultWithOutput(Result result, String output) { + var execAttrs = new ExecutableAttributesWithCapturedOutput(result.execAttrs(), output); + return result.copyWithExecutableAttributes(execAttrs); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java index 9727f21d2d7..3d24d293f18 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.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 @@ -30,6 +30,7 @@ import java.util.Objects; /** * Generic jpackage exception with non-null message. */ +@SelfContainedException public class JPackageException extends RuntimeException { public JPackageException(String msg) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.java index d0a4fd010e6..e7273d27ba5 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.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,8 +27,8 @@ package jdk.jpackage.internal.model; /** - * Generic package type. E.g.: application image, rpm, msi are all package types. + * Native package type. E.g.: dmg, rpm, msi are all package types. * * @see jdk.jpackage.internal.model.Package */ -public interface PackageType {} +public non-sealed interface PackageType extends BundleType {} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java new file mode 100644 index 00000000000..c7d1b3016ac --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.jpackage.internal.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for exceptions with self-contained error messages that don't + * require an additional context, like exception class name or a stack trace. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface SelfContainedException { +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.java index ccdeceb4a04..6fadc748ecc 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.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,19 +24,22 @@ */ package jdk.jpackage.internal.model; +import java.util.Objects; + /** * Standard native package types. */ public enum StandardPackageType implements PackageType { - WIN_MSI(".msi"), - WIN_EXE(".exe"), - LINUX_DEB(".deb"), - LINUX_RPM(".rpm"), - MAC_PKG(".pkg"), - MAC_DMG(".dmg"); + WIN_MSI("bundle-type.win-msi", ".msi"), + WIN_EXE("bundle-type.win-exe", ".exe"), + LINUX_DEB("bundle-type.linux-deb", ".deb"), + LINUX_RPM("bundle-type.linux-rpm", ".rpm"), + MAC_PKG("bundle-type.mac-pkg", ".pkg"), + MAC_DMG("bundle-type.mac-dmg", ".dmg"); - StandardPackageType(String suffix) { - this.suffix = suffix; + StandardPackageType(String key, String suffix) { + this.key = Objects.requireNonNull(key); + this.suffix = Objects.requireNonNull(suffix); } /** @@ -48,5 +51,11 @@ public enum StandardPackageType implements PackageType { return suffix; } + @Override + public String label() { + return I18N.getString(key); + } + + private final String key; private final String suffix; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index c5ea583514f..07460a18fe8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.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,14 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=Unknown bundle-type.win-app=Windows Application Image -bundle-type.win-exe=EXE Installer Package -bundle-type.win-msi=MSI Installer Package +bundle-type.win-exe=Windows EXE Installer +bundle-type.win-msi=Windows MSI Installer bundle-type.mac-app=Mac Application Image bundle-type.mac-dmg=Mac DMG Package bundle-type.mac-pkg=Mac PKG Package bundle-type.linux-app=Linux Application Image -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 resource.post-app-image-script=script to run after application image is populated @@ -43,13 +43,23 @@ message.using-default-resource=Using default package resource {0} {1} (add {2} t message.no-default-resource=No default package resource {0} (add {1} to the resource-dir to customize). message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). message.using-custom-resource=Using custom package resource {0} (loaded from {1}). -message.creating-app-bundle=Creating app package: {0} in {1} + +message.create-package=Building output package file... +message.create-app-image=Building output application image directory... +message.package-created=Succeeded in building output package file +message.app-image-created=Succeeded in building output application image directory + message.debug-working-directory=Kept working directory for debug: {0} -message.bundle-created=Succeeded in building {0} package + message.module-version=Using version "{0}" from module "{1}" as application version message.error-header=Error: {0} message.advice-header=Advice to fix: {0} +message.failed-command-output-header=Command output: + +error.command-failed-unexpected-output=Unexpected output from executing the command {0} +error.command-failed-unexpected-exit-code=Unexpected exit code {0} from executing the command {1} +error.command-failed-timed-out=Timed-out command {0} error.version-string-empty=Version may not be empty string error.version-string-zero-length-component=Version [{0}] contains a zero length component @@ -75,6 +85,7 @@ error.parameter-not-directory=The value "{0}" provided for parameter {1} is not error.parameter-not-empty-directory=The value "{0}" provided for parameter {1} is not an empty directory or non existent path error.parameter-not-url=The value "{0}" provided for parameter {1} is not a valid URL error.parameter-not-launcher-shortcut-dir=The value "{0}" provided for parameter {1} is not a valid shortcut startup directory +error.parameter-not-mac-bundle=The value "{0}" provided for parameter {1} is not a valid macOS bundle error.path-parameter-ioexception=I/O error accessing path value "{0}" of parameter {1} error.parameter-add-launcher-malformed=The value "{0}" provided for parameter {1} does not match the pattern = error.parameter-add-launcher-not-file=The value of path to a property file "{0}" provided for additional launcher "{1}" is not a valid file path @@ -96,7 +107,8 @@ error.tool-not-found.advice=Please install "{0}" error.tool-old-version=Can not find "{0}" {1} or newer error.tool-old-version.advice=Please install "{0}" {1} or newer -error.jlink.failed=jlink failed with: {0} +error.output-bundle-cannot-be-overwritten=Output package file "{0}" exists and can not be removed. + error.blocked.option=jlink option [{0}] is not permitted in --jlink-options error.no.name=Name not specified with --name and cannot infer one from app-image error.no.name.advice=Specify name with --name diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java new file mode 100644 index 00000000000..4d64030876f --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java @@ -0,0 +1,52 @@ +/* + * 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.jpackage.internal.util; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Formats command line arguments. + */ +public final class CommandLineFormat { + + public String format(List cmdline) { + return cmdline.stream().map(enquoter::applyTo).collect(Collectors.joining(" ")); + } + + public static CommandLineFormat platform() { + var format = new CommandLineFormat(); + format.enquoter = Enquoter.identity().setEnquotePredicate(Enquoter.QUOTE_IF_WHITESPACES).setQuoteChar('\''); + return format; + } + + private CommandLineFormat() { + } + + private Enquoter enquoter; + + public static final Function, String> DEFAULT = platform()::format; +} 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 new file mode 100644 index 00000000000..00fbdd71919 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java @@ -0,0 +1,1949 @@ +/* + * 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.jpackage.internal.util; + +import static java.util.stream.Collectors.joining; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.Predicate; +import java.util.spi.ToolProvider; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.internal.util.function.ThrowingRunnable; + +/** + * Runs commands and processes their stdout and stderr streams. + *

+ * A command is either a subprocess represented by {@link ProcessBuilder} or a + * tool provided by {@link ToolProvider}. + *

+ * A command is executed synchronously, and the result of its execution is + * stored in a {@link Result} instance which captures the exit code and any + * saved output streams. + *

+ * Depending on the configuration, it can save the entire output stream, only + * the first line, or not save the output at all. Stdout and stderr streams can + * be configured independently. + *

+ * Output streams can be treated as either byte streams or character streams. + * + *

+ * The table below shows how different parameter combinations affect the content + * written to streams returned by {@link #dumpStdout()} and + * {@link #dumpStderr()} for subsequently executed tools, regardless of whether + * their output streams are saved, or for subprocesses when the output streams + * are saved: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
discardStdout(false) and discardStderr(false)discardStdout(false) and discardStderr(true)discardStdout(true) and discardStderr(false)
redirectStderr(true) and dumpOutput(true) + *

+ * dumpStdout(): STDOUT and STDERR interleaved + *

+ * dumpStderr(): unchanged

+ *

+ * dumpStdout(): STDOUT + *

+ * dumpStderr(): unchanged

+ *

+ * dumpStdout(): STDERR; + *

+ * dumpStderr(): unchanged

redirectStderr(false) and dumpOutput(true) + *

+ * dumpStdout(): STDOUT + *

+ * dumpStderr(): STDERR

+ *

+ * dumpStdout(): STDOUT + *

+ * dumpStderr(): unchanged

+ *

+ * dumpStdout(): unchanged + *

+ * dumpStderr(): STDERR

+ * + *

+ * The table below shows how different parameter combinations affect the content + * written to the native file descriptors associated with {@link System#out} and + * {@link System#err} for subsequently executed subprocesses when the output + * streams are not saved: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
discardStdout(false) and discardStderr(false)discardStdout(false) and discardStderr(true)discardStdout(true) and discardStderr(false)
redirectStderr(true) and dumpOutput(true) + *

+ * System.out: STDOUT and STDERR interleaved + *

+ * System.err: unchanged

+ *

+ * System.out: STDOUT + *

+ * System.err: unchanged

+ *

+ * System.out: STDERR; + *

+ * The command's STDERR will be written to the stream referenced by + * {@link #dumpStdout()} rather than to the underlying file descriptor + * associated with the Java process's STDOUT + *

+ * System.err: unchanged

redirectStderr(false) and dumpOutput(true) + *

+ * System.out: STDOUT + *

+ * System.err: STDERR

+ *

+ * System.out: STDOUT + *

+ * System.err: unchanged

+ *

+ * System.out: unchanged + *

+ * System.err: STDERR

+ * + *

+ * The table below shows how different parameter combinations affect the + * properties of {@link Result} objects returned by + * {@link #execute(ProcessBuilder, long)} or + * {@link #execute(ToolProvider, long, String...)} when processing character + * streams: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
saveOutput(true)saveFirstLineOfOutput()
redirectStderr(true) and discardStdout(false) and + * discardStderr(false) + *

+ * content(): STDOUT and STDERR interleaved + *

+ * findStdout(): {@code Optional.empty()} + *

+ * findStderr(): {@code Optional.empty()}

+ *

+ * content(): a single-item list containing the first line of interleaved STDOUT + * and STDERR if the command produced any output; otherwise, an empty list + *

+ * findStdout(): {@code Optional.empty()} + *

+ * findStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(false) + *

+ * content(): STDOUT followed by STDERR + *

+ * stdout(): STDOUT + *

+ * stderr(): STDERR

+ *

+ * content(): a list containing at most two items: the first line of STDOUT (if + * the command produced any), followed by the first line of STDERR (if the + * command produced any) + *

+ * stdout(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

+ * findStderr(): The first line of STDERR (if the command produced any); + * otherwise an empty list + *

redirectStderr(true) and discardStdout(false) and + * discardStderr(true) + *

+ * content(): STDOUT + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

+ *

+ * content(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(true) + *

+ * content(): STDOUT + *

+ * stdout(): The same as content() + *

+ * stderr(): an empty list

+ *

+ * content(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

+ * stdout(): The same as content() + *

+ * stderr(): an empty list

redirectStderr(true) and discardStdout(true) and + * discardStderr(false) + *

+ * content(): STDERR + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

+ *

+ * content(): The first line of STDERR (if the command produced any); otherwise + * an empty list + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(true) and + * discardStderr(false) + *

+ * content(): STDERR + *

+ * findStdout(): an empty list + *

+ * stderr(): The same as content()

+ *

+ * content(): The first line of STDERR (if the command produced any); otherwise + * an empty list + *

+ * findStdout(): an empty list + *

+ * stderr(): The same as content()

+ *

+ * The table below shows how different parameter combinations affect the + * properties of {@link Result} objects returned by + * {@link #execute(ProcessBuilder, long)} or + * {@link #execute(ToolProvider, long, String...)} when processing byte streams: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
saveOutput(true) or saveFirstLineOfOutput()
redirectStderr(true) and discardStdout(false) and + * discardStderr(false) + *

+ * byteContent(): STDOUT and STDERR interleaved + *

+ * findByteStdout(): {@code Optional.empty()} + *

+ * findByteStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(false) + *

+ * byteContent(): STDOUT followed by STDERR + *

+ * byteStdout(): STDOUT + *

+ * byteStderr(): STDERR

redirectStderr(true) and discardStdout(false) and + * discardStderr(true) + *

+ * byteContent(): STDOUT + *

+ * byteStdout(): The same as byteContent() + *

+ * findByteStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(true) + *

+ * byteContent(): STDOUT + *

+ * byteStdout(): The same as byteContent() + *

+ * byteStderr(): an empty array

redirectStderr(true) and discardStdout(true) and + * discardStderr(false) + *

+ * byteContent(): STDERR + *

+ * byteStdout(): The same as byteContent() + *

+ * findByteStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(true) and + * discardStderr(false) + *

+ * byteContent(): STDERR + *

+ * findByteStdout(): an empty array + *

+ * byteStderr(): The same as byteContent()

+ */ +public final class CommandOutputControl { + + public CommandOutputControl() { + outputStreamsControl = new OutputStreamsControl(); + } + + private CommandOutputControl(CommandOutputControl other) { + flags = other.flags; + outputStreamsControl = other.outputStreamsControl.copy(); + dumpStdout = other.dumpStdout; + dumpStderr = other.dumpStderr; + charset = other.charset; + processListener = other.processListener; + } + + /** + * Specifies whether the full output produced by commands subsequently executed + * by this object will be saved. + *

+ * If {@code v} is {@code true}, both stdout and stderr streams will be saved; + * otherwise, they will not be saved. + *

+ * This setting is mutually exclusive with {@link #saveFirstLineOfOutput()}. + * + * @param v {@code true} to save the full stdout and stderr streams; + * {@code false} otherwise + * @return this + */ + public CommandOutputControl saveOutput(boolean v) { + return setOutputControl(v, OutputControlOption.SAVE_ALL); + } + + /** + * Returns whether this object will save the complete output of commands + * subsequently executed. + * + * @return {@code true} if this object will save the full output of commands it + * executes subsequently; {@code false} otherwise + */ + public boolean isSaveOutput() { + return outputStreamsControl.stdout().saveAll(); + } + + /** + * Specifies whether the first line of the output, combined from the stdout and + * stderr streams of commands subsequently executed by this object, will be + * saved. + *

+ * This setting is mutually exclusive with {@link #saveOutput(boolean)}. + * + * @return this + */ + public CommandOutputControl saveFirstLineOfOutput() { + return setOutputControl(true, OutputControlOption.SAVE_FIRST_LINE); + } + + /** + * Returns whether this object will save the first line of the output of + * commands subsequently executed. + * + * @return {@code true} if this object will save the first line of the output of + * commands it executes subsequently; {@code false} otherwise + */ + public boolean isSaveFirstLineOfOutput() { + return outputStreamsControl.stdout().saveFirstLine(); + } + + /** + * Specifies whether this object will dump the output streams from + * subsequently executed commands into the streams returned by + * {@link #dumpStdout()} and {@link #dumpStdout()} methods respectively. + *

+ * If this object is configured to redirect stderr of subsequently executed + * commands into their stdout ({@code redirectStderr(true)}), their output + * streams will be dumped into the stream returned by {@code dumpStdout()} + * method. Otherwise, their stdout and stderr streams will be dumped into the + * stream returned by {@code dumpStdout()} and {@code dumpStderr()} methods + * respectively. + * + * @param v if output streams from subsequently executed commands will be + * dumped into streams returned by {@code dumpStdout()} and + * {@code dumpStderr()} methods respectively + * + * @return this + * + * @see #redirectStderr(boolean) + * @see #dumpStdout() + * @see #dumpStderr() + */ + public CommandOutputControl dumpOutput(boolean v) { + setFlag(Flag.DUMP, v); + return setOutputControl(v, OutputControlOption.DUMP); + } + + /** + * Returns the value passed in the last call of {@link #dumpOutput(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #dumpOutput(boolean)} + */ + public boolean isDumpOutput() { + return Flag.DUMP.isSet(flags); + } + + /** + * Specifies whether this object will treat output streams of subsequently + * executed commands as byte streams rather than character streams. + * + * @param v {@code true} if this object will treat the output streams of + * subsequently executed commands as byte streams, and {@code false} + * otherwise + * + * @return this + */ + public CommandOutputControl binaryOutput(boolean v) { + return setFlag(Flag.BINARY_OUTPUT, v); + } + + /** + * Returns the value passed in the last call of {@link #binaryOutput(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #binaryOutput(boolean)} + */ + public boolean isBinaryOutput() { + return Flag.BINARY_OUTPUT.isSet(flags); + } + + /** + * Sets character encoding that will be applied to the stdout and the stderr + * streams of commands (subprocesses and {@code ToolProvider}-s) subsequently + * executed by this object. The default encoding is {@code UTF-8}. + *

+ * The value will be ignored if this object is configured for byte output + * streams. + * + * @param v character encoding for output streams of subsequently executed + * commands + * + * @see #binaryOutput(boolean) + * + * @return this + */ + public CommandOutputControl charset(Charset v) { + charset = v; + return this; + } + + /** + * Returns the value passed in the last call of + * {@link #charset(Charset)} method on this object, or + * {@link StandardCharsets#UTF_8} if the method has not been called. + * + * @return the character encoding that will be applied to the stdout and stderr + * streams of commands subsequently executed by this object + */ + public Charset charset() { + return Optional.ofNullable(charset).orElse(StandardCharsets.UTF_8); + } + + /** + * Specifies whether the stderr stream will be redirected into the stdout stream + * for commands subsequently executed by this object. + * + * @see ProcessBuilder#redirectErrorStream(boolean) + * + * @param v {@code true} if the stderr stream of commands subsequently executed + * by this object will be redirected into the stdout stream; + * {@code false} otherwise + * + * @return this + */ + public CommandOutputControl redirectStderr(boolean v) { + return setFlag(Flag.REDIRECT_STDERR, v); + } + + /** + * Returns the value passed in the last call of {@link #redirectStderr(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #redirectStderr(boolean)} + */ + public boolean isRedirectStderr() { + return Flag.REDIRECT_STDERR.isSet(flags); + } + + /** + * Specifies whether stderr and stdout streams for subprocesses subsequently + * executed by this object will be stored in files. + *

+ * By default, if an output stream of a subprocess is configured for saving, + * this object will retrieve the content using {@link Process#getInputStream()} + * function for stdout and {@link Process#getErrorStream()} function for stderr. + * However, these functions don't always work correctly due to a + * JDK-8236825 bug + * still reproducible on macOS JDK26. The alternative way to get the content of + * output streams of a subprocess is to redirect them into files and read these + * files when the subprocess terminates. + *

+ * It will use {@code Files.createTempFile("jpackageOutputTempFile", ".tmp")} to + * create a file for each subprocess's output stream configured for saving. All + * created files will be automatically deleted at the exit of + * {@link #execute(ProcessBuilder, long)} method. + *

+ * Doesn't apply to executing {@code ToolProvider}-s. + *

+ * Storing output streams in files takes longer than managing them in memory and + * should be avoided if possible. + * + * @param v {@code true} if this object will use files to store saved output + * streams of subsequently executed subprocesses; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl storeOutputInFiles(boolean v) { + return setFlag(Flag.STORE_OUTPUT_IN_FILES, v); + } + + /** + * Returns the value passed in the last call of {@link #storeOutputInFiles(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #storeOutputInFiles(boolean)} + */ + public boolean isStoreOutputInFiles() { + return Flag.STORE_OUTPUT_IN_FILES.isSet(flags); + } + + /** + * Specifies whether stdout streams from commands subsequently executed by this + * object will be discarded. + * + * @param v {@code true} if this object will discard stdout streams from + * commands subsequently executed by this object; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl discardStdout(boolean v) { + setFlag(Flag.DISCARD_STDOUT, v); + outputStreamsControl.stdout().discard(v); + return this; + } + + /** + * Returns the value passed in the last call of {@link #discardStdout(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #discardStdout(boolean)} + */ + public boolean isDiscardStdout() { + return Flag.DISCARD_STDOUT.isSet(flags); + } + + /** + * Specifies whether stderr streams from commands subsequently executed by this + * object will be discarded. + * + * @param v {@code true} if this object will discard stderr streams from + * commands subsequently executed by this object; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl discardStderr(boolean v) { + setFlag(Flag.DISCARD_STDERR, v); + outputStreamsControl.stderr().discard(v); + return this; + } + + /** + * Returns the value passed in the last call of {@link #discardStderr(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #discardStderr(boolean)} + */ + public boolean isDiscardStderr() { + return Flag.DISCARD_STDERR.isSet(flags); + } + + /** + * Specifies the stream where stdout streams from commands subsequently executed + * by this object will be dumped. + *

+ * If the {@code null} is specified and this object configuration is equivalent + * to {@code dumpOutput(true).saveOutput(false).discardStdout(false)} the stdout + * streams from commands subsequently executed by this object will be written + * into the file descriptor associated with the {@code Systsem.out} stream. If + * you want them to be written into the {@code Systsem.out} object, pass the + * {@code Systsem.out} reference into this function. + * + * @param v the stream where stdout streams from commands subsequently executed + * by this object will be dumped; {@code null} permitted + * @return this + */ + public CommandOutputControl dumpStdout(PrintStream v) { + dumpStdout = v; + return this; + } + + /** + * Returns the value passed in the last call of {@link #dumpStdout(PrintStream)} + * method on this object, or {@link System#out} if the method has not been + * called. + * + * @return the stream where stdout streams from commands subsequently executed + * by this object will be dumped + */ + public PrintStream dumpStdout() { + return Optional.ofNullable(dumpStdout).orElse(System.out); + } + + /** + * Specifies the stream where stderr streams from commands subsequently executed + * by this object will be dumped. + *

+ * If the {@code null} is specified and this object configuration is equivalent + * to + * {@code dumpOutput(true).saveOutput(false).redirectStderr(false).discardStderr(false)} + * the stderr streams from commands subsequently executed by this object will be + * written into the file descriptor associated with the {@code Systsem.err} + * stream. If you want them to be written into the {@code Systsem.err} object, + * pass the {@code Systsem.err} reference into this function. + * + * @param v the stream where stderr streams from commands subsequently executed + * by this object will be dumped; {@code null} permitted + * @return this + */ + public CommandOutputControl dumpStderr(PrintStream v) { + dumpStderr = v; + return this; + } + + /** + * Returns the value passed in the last call of {@link #dumpStderr(PrintStream)} + * method on this object, or {@link System#err} if the method has not been + * called. + * + * @return the stream where stderr streams from commands subsequently executed + * by this object will be dumped + */ + public PrintStream dumpStderr() { + return Optional.ofNullable(dumpStderr).orElse(System.err); + } + + /** + * Sets the callback to be invoked when this object starts a subprocess from + * subsequent {@link #execute(ProcessBuilder, long)} calls. + * + *

+ * This object maintains a single callback. Calling this method replaces any + * previously set callback. + * + *

+ * The callback is invoked on the thread that calls + * {@link #execute(ProcessBuilder, long)} after the subprocess's output streams + * begin being pumped. + * + * @param v the callback for notifying a subprocess being started or + * {@code null} + * @return this + */ + public CommandOutputControl processListener(Consumer v) { + processListener = v; + return this; + } + + /** + * Returns an {@code Optional} wrapping the value passed in the last call of + * {@link #processListener(Consumer)} method on this object, or an empty + * {@code Optional} if the method has not been called or {@code null} was passed in the last call. + * + * @return an {@code Optional} wrapping the value passed in the last call of + * {@link #processListener(Consumer)} + */ + public Optional> processListener() { + return Optional.ofNullable(processListener); + } + + /** + * Returns a deep copy of this object. Changes to the copy will not affect the + * original. + * + * @return a deep copy of this object + */ + public CommandOutputControl copy() { + return new CommandOutputControl(this); + } + + public interface ExecutableAttributes { + List commandLine(); + + default String printableCommandLine() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } + } + + public sealed interface Executable { + + ExecutableAttributes attributes(); + + Result execute() throws IOException, InterruptedException; + + Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException; + } + + public record ProcessAttributes(Optional pid, List commandLine) implements ExecutableAttributes { + public ProcessAttributes { + Objects.requireNonNull(pid); + commandLine.forEach(Objects::requireNonNull); + } + } + + public record ToolProviderAttributes(String name, List args) implements ExecutableAttributes { + public ToolProviderAttributes { + Objects.requireNonNull(name); + args.forEach(Objects::requireNonNull); + } + + @Override + public List commandLine() { + return Stream.concat(Stream.of(name), args.stream()).toList(); + } + } + + public static ExecutableAttributes EMPTY_EXECUTABLE_ATTRIBUTES = new ExecutableAttributes() { + @Override + public List commandLine() { + return List.of(""); + } + }; + + public Executable createExecutable(ToolProvider tp, String... args) { + return new ToolProviderExecutable(tp, List.of(args), this); + } + + public Executable createExecutable(ProcessBuilder pb) { + return new ProcessExecutable(pb, this); + } + + public record Result( + Optional exitCode, + Optional>> output, + Optional> byteOutput, + ExecutableAttributes execAttrs) { + + public Result { + Objects.requireNonNull(exitCode); + Objects.requireNonNull(output); + Objects.requireNonNull(byteOutput); + Objects.requireNonNull(execAttrs); + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + public Result create() { + return new Result( + exitCode, + Optional.ofNullable(content).map(List::copyOf).map(StringListContent::new).map(c -> { + return new CommandOutput>(Optional.of(c), c.size(), true); + }), + Optional.empty(), + Optional.ofNullable(execAttrs).orElse(EMPTY_EXECUTABLE_ATTRIBUTES)); + } + + public Builder exitCode(int v) { + exitCode = Optional.of(v); + return this; + } + + public Builder noExitCode() { + exitCode = Optional.empty(); + return this; + } + + public Builder execAttrs(ExecutableAttributes v) { + execAttrs = v; + return this; + } + + public Builder content(List v) { + content = v; + return this; + } + + public Builder content(String... v) { + return content(List.of(v)); + } + + private ExecutableAttributes execAttrs; + private List content; + private Optional exitCode = Optional.empty(); + } + + public Result(int exitCode) { + this(Optional.of(exitCode), Optional.empty(), Optional.empty(), EMPTY_EXECUTABLE_ATTRIBUTES); + } + + public int getExitCode() { + return exitCode.orElseThrow(() -> { + return new IllegalStateException("Exit code is unavailable for timed-out command"); + }); + } + + public Result expectExitCode(int main, int... other) throws UnexpectedExitCodeException { + return expectExitCode(v -> { + return IntStream.concat(IntStream.of(main), IntStream.of(other)).boxed().anyMatch(Predicate.isEqual(v)); + }); + } + + public Result expectExitCode(Collection expected) throws UnexpectedExitCodeException { + return expectExitCode(expected::contains); + } + + public Result expectExitCode(IntPredicate expected) throws UnexpectedExitCodeException { + if (!expected.test(getExitCode())) { + throw new UnexpectedExitCodeException(this); + } + return this; + } + + public UnexpectedResultException unexpected() { + return new UnexpectedResultException(this); + } + + public UnexpectedResultException unexpected(String message) { + return new UnexpectedResultException(this, message); + } + + public Optional> findContent() { + return output.flatMap(CommandOutput::combined); + } + + public Optional> findStdout() { + return output.flatMap(CommandOutput::stdout); + } + + public Optional> findStderr() { + return output.flatMap(CommandOutput::stderr); + } + + // For backward compatibility + public List getOutput() { + return content(); + } + + public List content() { + return findContent().orElseThrow(); + } + + public List stdout() { + return findStdout().orElseThrow(); + } + + public List stderr() { + return findStderr().orElseThrow(); + } + + public Optional findByteContent() { + return byteOutput.flatMap(CommandOutput::combined); + } + + public Optional findByteStdout() { + return byteOutput.flatMap(CommandOutput::stdout); + } + + public Optional findByteStderr() { + return byteOutput.flatMap(CommandOutput::stderr); + } + + public byte[] byteContent() { + return findByteContent().orElseThrow(); + } + + public byte[] byteStdout() { + return findByteStdout().orElseThrow(); + } + + public byte[] byteStderr() { + return findByteStderr().orElseThrow(); + } + + public Result toCharacterResult(Charset charset, boolean keepByteContent) throws IOException { + Objects.requireNonNull(charset); + + if (byteOutput.isEmpty()) { + return this; + } + + var theByteOutput = byteOutput.get(); + + try { + Optional>> out; + if (theByteOutput.content().isEmpty()) { + // The content is unavailable. + out = Optional.empty(); + } else if (theByteOutput.stdoutContentSize() == 0) { + // The content is available, but empty. + out = Optional.of(new StringListContent(List.of())); + } else if (theByteOutput.interleaved()) { + // STDOUT and STDERR streams are interleaved. + out = theByteOutput.combined().map(data -> { + return toStringList(data, charset); + }); + } else { + // Non-empty STDOUT not interleaved with STDERR. + out = findByteStdout().map(data -> { + return toStringList(data, charset); + }); + } + + var err = findByteStderr().map(data -> { + return toStringList(data, charset); + }); + + var newOutput = combine(out, err, theByteOutput.interleaved); + + return new Result(exitCode, Optional.of(newOutput), byteOutput.filter(_ -> keepByteContent), execAttrs); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + public Result copyWithExecutableAttributes(ExecutableAttributes execAttrs) { + return new Result(exitCode, output, byteOutput, Objects.requireNonNull(execAttrs)); + } + + private static StringListContent toStringList(byte[] data, Charset charset) { + try (var bufReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), charset))) { + return new StringListContent(bufReader.lines().toList()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + public static sealed class UnexpectedResultException extends IOException { + + private UnexpectedResultException(Result value, String message) { + super(Objects.requireNonNull(message)); + this.value = Objects.requireNonNull(value); + } + + private UnexpectedResultException(Result value) { + this(value, String.format("Unexpected result from executing the command %s", + value.execAttrs().printableCommandLine())); + } + + public Result getResult() { + return value; + } + + private final transient Result value; + + private static final long serialVersionUID = 1L; + } + + public static final class UnexpectedExitCodeException extends UnexpectedResultException { + + public UnexpectedExitCodeException(Result value, String message) { + super(requireExitCode(value), message); + } + + public UnexpectedExitCodeException(Result value) { + this(value, String.format("Unexpected exit code %d from executing the command %s", + requireExitCode(value).getExitCode(), value.execAttrs().printableCommandLine())); + } + + private static Result requireExitCode(Result v) { + Objects.requireNonNull(v); + if (v.exitCode().isPresent()) { + return v; + } else { + throw new IllegalArgumentException(); + } + } + + private static final long serialVersionUID = 1L; + } + + public String description() { + var tokens = outputStreamsControl.descriptionTokens(); + if (isBinaryOutput()) { + tokens.add("byte"); + } + if (redirectRetainedStderr()) { + tokens.add("interleave"); + } + return String.join("; ", tokens); + } + + private Result execute(ProcessBuilder pb, long timeoutMillis) + throws IOException, InterruptedException { + + Objects.requireNonNull(pb); + + var theCharset = charset(); + + configureProcessBuilder(pb); + + var csc = new CachingStreamsConfig(); + + var process = pb.start(); + + BiConsumer gobbler = (in, ps) -> { + try { + if (isBinaryOutput()) { + try (in) { + in.transferTo(ps); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else { + try (var bufReader = new BufferedReader(new InputStreamReader(in, theCharset))) { + bufReader.lines().forEach(ps::println); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } finally { + suppressIOException(ps::flush); + } + }; + + // Start fetching process output streams. + // Do it before waiting for the process termination to avoid deadlocks. + + final Optional> stdoutGobbler; + if (mustReadOutputStream(pb.redirectOutput())) { + stdoutGobbler = Optional.of(CompletableFuture.runAsync(() -> { + gobbler.accept(process.getInputStream(), csc.out()); + }, gobblerExecutor)); + } else { + stdoutGobbler = Optional.empty(); + } + + final Optional> stderrGobbler; + if (!pb.redirectErrorStream() && mustReadOutputStream(pb.redirectError())) { + stderrGobbler = Optional.of(CompletableFuture.runAsync(() -> { + gobbler.accept(process.getErrorStream(), csc.err()); + }, gobblerExecutor)); + } else { + stderrGobbler = Optional.empty(); + } + + processListener().ifPresent(c -> { + c.accept(process); + }); + + final Optional exitCode; + if (timeoutMillis < 0) { + exitCode = Optional.of(process.waitFor()); + } else if (!process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)) { + // Destroy the process and cancel the process output stream gobblers. + process.destroy(); + for (var g : List.of(stdoutGobbler, stderrGobbler)) { + g.ifPresent(future -> { + future.cancel(true); + }); + } + exitCode = Optional.empty(); + } else { + exitCode = Optional.of(process.exitValue()); + } + + try { + if (isStoreOutputInFiles()) { + var stdoutStorage = streamFileSink(pb.redirectOutput()); + var stderrStorage = streamFileSink(pb.redirectError()); + + Function toInputStream = path -> { + try { + return Files.newInputStream(path); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }; + + try { + stdoutStorage.map(toInputStream).ifPresent(in -> { + gobbler.accept(in, csc.out()); + }); + + stderrStorage.map(toInputStream).ifPresent(in -> { + gobbler.accept(in, csc.err()); + }); + } finally { + Consumer silentDeleter = path -> { + suppressIOException(Files::delete, path); + }; + + stdoutStorage.ifPresent(silentDeleter); + stderrStorage.ifPresent(silentDeleter); + } + } else { + stdoutGobbler.ifPresent(CommandOutputControl::join); + stderrGobbler.ifPresent(CommandOutputControl::join); + } + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + + return csc.createResult(exitCode, new ProcessAttributes(getPID(process), pb.command())); + } + + private Result execute(ToolProvider tp, long timeoutMillis, String... args) + throws IOException, InterruptedException { + + var csc = new CachingStreamsConfig(); + + Optional exitCode; + var out = csc.out(); + var err = csc.err(); + try { + if (timeoutMillis < 0) { + exitCode = Optional.of(tp.run(out, err, args)); + } else { + var future = new CompletableFuture>(); + + var workerThread = Thread.ofVirtual().start(() -> { + Optional result = Optional.empty(); + try { + result = Optional.of(tp.run(out, err, args)); + } catch (Exception ex) { + future.completeExceptionally(ex); + return; + } + future.complete(result); + }); + + try { + exitCode = future.get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (ExecutionException ex) { + // Rethrow the cause (ex.getCause()) as a RuntimeException. + // If `ex.getCause()` returns an Error, ExceptionBox.unbox() will throw it. + throw ExceptionBox.toUnchecked(ExceptionBox.unbox(ex.getCause())); + } catch (TimeoutException ex) { + workerThread.interrupt(); + exitCode = Optional.empty(); + } + } + } finally { + suppressIOException(out::flush); + suppressIOException(err::flush); + } + + return csc.createResult(exitCode, new ToolProviderAttributes(tp.name(), List.of(args))); + } + + private CommandOutputControl setOutputControl(boolean set, OutputControlOption v) { + outputStreamsControl.stdout().set(set, v); + outputStreamsControl.stderr().set(set, v); + return this; + } + + private CommandOutputControl setFlag(Flag flag, boolean v) { + flags = flag.set(flags, v); + return this; + } + + private Optional streamFileSink(ProcessBuilder.Redirect redirect) { + return Optional.of(redirect) + .filter(Predicate.isEqual(ProcessBuilder.Redirect.DISCARD).negate()) + .map(ProcessBuilder.Redirect::file) + .map(File::toPath); + } + + private void configureProcessBuilder(ProcessBuilder pb) throws IOException { + + var stdoutRedirect = outputStreamsControl.stdout().asProcessBuilderRedirect(); + var stderrRedirect = outputStreamsControl.stderr().asProcessBuilderRedirect(); + + if (!stdoutRedirect.equals(stderrRedirect) && Stream.of( + stdoutRedirect, + stderrRedirect + ).noneMatch(Predicate.isEqual(ProcessBuilder.Redirect.DISCARD)) && redirectRetainedStderr()) { + throw new IllegalStateException(String.format( + "Can't redirect stderr into stdout because they have different redirects: stdout=%s; stderr=%s", + stdoutRedirect, stderrRedirect)); + } + + pb.redirectErrorStream(redirectRetainedStderr()); + if (replaceStdoutWithStderr()) { + if (stderrRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stderrRedirect = ProcessBuilder.Redirect.PIPE; + } + pb.redirectErrorStream(false); + } + + stdoutRedirect = mapRedirect(stdoutRedirect); + stderrRedirect = mapRedirect(stderrRedirect); + + if (dumpStdout != null && stdoutRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stdoutRedirect = ProcessBuilder.Redirect.PIPE; + } + + if (dumpStderr != null && stderrRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stderrRedirect = ProcessBuilder.Redirect.PIPE; + } + + pb.redirectOutput(stdoutRedirect); + pb.redirectError(stderrRedirect); + } + + private ProcessBuilder.Redirect mapRedirect(ProcessBuilder.Redirect redirect) throws IOException { + if (isStoreOutputInFiles() && redirect.equals(ProcessBuilder.Redirect.PIPE)) { + var sink = Files.createTempFile("jpackageOutputTempFile", ".tmp"); + return ProcessBuilder.Redirect.to(sink.toFile()); + } else { + return redirect; + } + } + + /** + * Returns {@code true} if STDERR is not discarded and will be redirected to STDOUT, and {@code false} otherwise. + */ + private boolean redirectRetainedStderr() { + return isRedirectStderr() && !outputStreamsControl.stderr().discard(); + } + + /** + * Returns {@code true} if STDERR will replace STDOUT, and {@code false} otherwise. + *

+ * STDERR will replace STDOUT if it is redirected and not discarded, and if STDOUT is discarded. + */ + private boolean replaceStdoutWithStderr() { + return redirectRetainedStderr() && outputStreamsControl.stdout().discard(); + } + + private static T join(CompletableFuture future, T cancelledValue) { + Objects.requireNonNull(future); + try { + return future.join(); + } catch (CancellationException ex) { + return cancelledValue; + } catch (CompletionException ex) { + switch (ExceptionBox.unbox(ex.getCause())) { + case IOException cause -> { + throw new UncheckedIOException(cause); + } + case UncheckedIOException cause -> { + throw cause; + } + case Exception cause -> { + throw ExceptionBox.toUnchecked(cause); + } + } + } + } + + private static void join(CompletableFuture future) { + join(future, null); + } + + private static boolean mustReadOutputStream(ProcessBuilder.Redirect redirect) { + return redirect.equals(ProcessBuilder.Redirect.PIPE); + } + + private static Optional> read(OutputControl outputControl, CachingPrintStream cps) throws IOException { + final var bufferAsString = cps.bufferContents(); + try (final var bufReader = new BufferedReader(new StringReader(bufferAsString.orElse("")))) { + if (outputControl.saveFirstLine()) { + return Optional.of(bufReader.lines().findFirst().map(List::of).orElseGet(List::of)); + } else if (outputControl.saveAll()) { + return Optional.of(bufReader.lines().toList()); + } else { + return Optional.empty(); + } + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + private static Optional readBinary(OutputControl outputControl, CachingPrintStream cps) { + if (outputControl.save()) { + return cps.buf().map(ByteArrayOutputStream::toByteArray).or(() -> { + return Optional.of(new byte[0]); + }); + } else { + return Optional.empty(); + } + } + + private static CommandOutput combine( + Optional> out, + Optional> err, + boolean interleaved) { + + if (out.isEmpty() && err.isEmpty()) { + return CommandOutput.empty(); + } else if (out.isEmpty()) { + // This branch is unreachable because it is impossible to make it save stderr without saving stdout. + // If streams are configured for saving and stdout is discarded, + // its saved contents will be an Optional instance wrapping an empty content, not an empty Optional. + throw ExceptionBox.reachedUnreachable(); + } else if (err.isEmpty()) { + return new CommandOutput<>(out, Integer.MAX_VALUE, interleaved); + } else { + final var combined = out.get().append(err.get()); + return new CommandOutput<>(Optional.of(combined), out.orElseThrow().size(), interleaved); + } + } + + private static PrintStream nullPrintStream() { + return new PrintStream(OutputStream.nullOutputStream()); + } + + private sealed interface Content { + T data(); + int size(); + Content slice(int from, int to); + Content append(Content other); + } + + private record StringListContent(List data) implements Content> { + StringListContent { + Objects.requireNonNull(data); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public StringListContent slice(int from, int to) { + return new StringListContent(data.subList(from, to)); + } + + @Override + public StringListContent append(Content> other) { + return new StringListContent(Stream.of(data, other.data()).flatMap(List::stream).toList()); + } + } + + private record ByteContent(byte[] data) implements Content { + ByteContent { + Objects.requireNonNull(data); + } + + @Override + public int size() { + return data.length; + } + + @Override + public ByteContent slice(int from, int to) { + return new ByteContent(Arrays.copyOfRange(data, from, to)); + } + + @Override + public ByteContent append(Content other) { + byte[] combined = new byte[size() + other.size()]; + System.arraycopy(data, 0, combined, 0, data.length); + System.arraycopy(other.data(), 0, combined, data.length, other.size()); + return new ByteContent(combined); + } + } + + private record OutputStreamsControl(OutputControl stdout, OutputControl stderr) { + OutputStreamsControl { + Objects.requireNonNull(stdout); + Objects.requireNonNull(stderr); + } + + OutputStreamsControl() { + this(new OutputControl(), new OutputControl()); + } + + OutputStreamsControl copy() { + return new OutputStreamsControl(stdout.copy(), stderr.copy()); + } + + List descriptionTokens() { + final List tokens = new ArrayList<>(); + if (stdout.save()) { // Save flags are the same for stdout and stderr, checking stdout is sufficient. + streamsLabel("save ", true).ifPresent(tokens::add); + } + if (stdout.dump() || stderr.dump()) { + streamsLabel("echo ", true).ifPresent(tokens::add); + } + streamsLabel("discard ", false).ifPresent(tokens::add); + if (tokens.isEmpty()) { + // Unreachable because there is always at least one token in the description. + throw ExceptionBox.reachedUnreachable(); + } else { + return tokens; + } + } + + private Optional streamsLabel(String prefix, boolean negate) { + Objects.requireNonNull(prefix); + final var str = Stream.of(stdoutLabel(negate), stderrLabel(negate)) + .filter(Optional::isPresent) + .map(Optional::orElseThrow) + .collect(joining("+")); + if (str.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(prefix + str); + } + } + + private Optional stdoutLabel(boolean negate) { + if ((stdout.discard() && !negate) || (!stdout.discard() && negate)) { + return Optional.of("out"); + } else { + return Optional.empty(); + } + } + + private Optional stderrLabel(boolean negate) { + if ((stderr.discard() && !negate) || (!stderr.discard() && negate)) { + return Optional.of("err"); + } else { + return Optional.empty(); + } + } + } + + private record CachingPrintStream(PrintStream ps, Optional buf) { + CachingPrintStream { + Objects.requireNonNull(ps); + Objects.requireNonNull(buf); + } + + Optional bufferContents() { + return buf.map(ByteArrayOutputStream::toString); + } + + static Builder build(Charset charset) { + return new Builder(charset); + } + + static final class Builder { + + private Builder(Charset charset) { + this.charset = Objects.requireNonNull(charset); + } + + Builder save(boolean v) { + save = v; + return this; + } + + Builder discard(boolean v) { + discard = v; + return this; + } + + Builder dumpStream(PrintStream v) { + dumpStream = v; + return this; + } + + Builder buffer(ByteArrayOutputStream v) { + externalBuffer = v; + return this; + } + + CachingPrintStream create() { + final Optional buf; + if (save && !discard) { + buf = Optional.ofNullable(externalBuffer).or(() -> { + return Optional.of(new ByteArrayOutputStream()); + }); + } else { + buf = Optional.empty(); + } + + final PrintStream ps; + if (buf.isPresent() && dumpStream != null) { + ps = new PrintStream(new TeeOutputStream(List.of(buf.get(), dumpStream)), true, dumpStream.charset()); + } else if (!discard) { + ps = buf.map(in -> { + return new PrintStream(in, false, charset); + }).or(() -> { + return Optional.ofNullable(dumpStream); + }).orElseGet(CommandOutputControl::nullPrintStream); + } else { + ps = nullPrintStream(); + } + + return new CachingPrintStream(ps, buf); + } + + private boolean save; + private boolean discard; + private PrintStream dumpStream; + private ByteArrayOutputStream externalBuffer; + private final Charset charset; + } + } + + private final class CachingStreamsConfig { + + CachingStreamsConfig() { + out = outputStreamsControl.stdout().buildCachingPrintStream(dumpStdout(), charset()).create(); + if (isRedirectStderr()) { + var builder = outputStreamsControl.stderr().buildCachingPrintStream(dumpStdout(), charset()); + out.buf().ifPresent(builder::buffer); + err = builder.create(); + } else { + err = outputStreamsControl.stderr().buildCachingPrintStream(dumpStderr(), charset()).create(); + } + } + + Result createResult(Optional exitCode, ExecutableAttributes execAttrs) throws IOException { + + CommandOutput> output; + CommandOutput byteOutput; + + CachingPrintStream effectiveOut; + if (out.buf().isEmpty() && isRedirectStderr()) { + effectiveOut = new CachingPrintStream(nullPrintStream(), err.buf()); + } else { + effectiveOut = out; + } + + if (isBinaryOutput()) { + Optional outContent, errContent; + if (isRedirectStderr()) { + outContent = readBinary(outputStreamsControl.stdout(), effectiveOut).map(ByteContent::new); + errContent = Optional.empty(); + } else { + outContent = readBinary(outputStreamsControl.stdout(), out).map(ByteContent::new); + errContent = readBinary(outputStreamsControl.stderr(), err).map(ByteContent::new); + } + + byteOutput = combine(outContent, errContent, redirectRetainedStderr()); + output = null; + } else { + Optional outContent, errContent; + if (isRedirectStderr()) { + outContent = read(outputStreamsControl.stdout(), effectiveOut).map(StringListContent::new); + errContent = Optional.empty(); + } else { + outContent = read(outputStreamsControl.stdout(), out).map(StringListContent::new); + errContent = read(outputStreamsControl.stderr(), err).map(StringListContent::new); + } + + output = combine(outContent, errContent, redirectRetainedStderr()); + byteOutput = null; + } + + return new Result(exitCode, Optional.ofNullable(output), Optional.ofNullable(byteOutput), execAttrs); + } + + PrintStream out() { + return out.ps(); + } + + PrintStream err() { + return err.ps(); + } + + private final CachingPrintStream out; + private final CachingPrintStream err; + } + + private static final class OutputControl { + + OutputControl() { + } + + private OutputControl(OutputControl other) { + dump = other.dump; + discard = other.discard; + save = other.save; + } + + boolean save() { + return save.isPresent(); + } + + boolean saveAll() { + return save.orElse(null) == OutputControlOption.SAVE_ALL; + } + + boolean saveFirstLine() { + return save.orElse(null) == OutputControlOption.SAVE_FIRST_LINE; + } + + boolean discard() { + return discard || (!dump && save.isEmpty()); + } + + boolean dump() { + return !discard && dump; + } + + OutputControl dump(boolean v) { + this.dump = v; + return this; + } + + OutputControl discard(boolean v) { + this.discard = v; + return this; + } + + OutputControl saveAll(boolean v) { + if (v) { + save = Optional.of(OutputControlOption.SAVE_ALL); + } else { + save = Optional.empty(); + } + return this; + } + + OutputControl saveFirstLine(boolean v) { + if (v) { + save = Optional.of(OutputControlOption.SAVE_FIRST_LINE); + } else { + save = Optional.empty(); + } + return this; + } + + OutputControl set(boolean set, OutputControlOption v) { + switch (v) { + case DUMP -> dump(set); + case SAVE_ALL -> saveAll(set); + case SAVE_FIRST_LINE -> saveFirstLine(set); + } + return this; + } + + OutputControl copy() { + return new OutputControl(this); + } + + ProcessBuilder.Redirect asProcessBuilderRedirect() { + if (discard()) { + return ProcessBuilder.Redirect.DISCARD; + } else if (dump && !save()) { + return ProcessBuilder.Redirect.INHERIT; + } else { + return ProcessBuilder.Redirect.PIPE; + } + } + + CachingPrintStream.Builder buildCachingPrintStream(PrintStream dumpStream, Charset charset) { + Objects.requireNonNull(dumpStream); + final var builder = CachingPrintStream.build(charset).save(save()).discard(discard()); + if (dump()) { + builder.dumpStream(dumpStream); + } + return builder; + } + + private boolean dump; + private boolean discard; + private Optional save = Optional.empty(); + } + + private record CommandOutput(Optional> content, int stdoutContentSize, boolean interleaved) { + + CommandOutput { + Objects.requireNonNull(content); + if (interleaved) { + stdoutContentSize = content.map(Content::size).orElse(-1); + } + } + + CommandOutput() { + this(Optional.empty(), 0, false); + } + + Optional combined() { + return content.map(Content::data); + } + + /** + * Returns non-empty {@code Optional} if stdout is available and stdout and stderr are not interleaved. + * @return stdout if it can be extracted from the combined output + */ + Optional stdout() { + if (withoutExtractableStdout()) { + return Optional.empty(); + } + + final var theContent = content.orElseThrow(); + if (stdoutContentSize == theContent.size()) { + return combined(); + } else { + return Optional.of(theContent.slice(0, Integer.min(stdoutContentSize, theContent.size())).data()); + } + } + + /** + * Returns non-empty {@code Optional} if stderr is available and stdout and stderr are not interleaved. + * @return stderr if it can be extracted from the combined output + */ + Optional stderr() { + if (withoutExtractableStderr()) { + return Optional.empty(); + } else if (stdoutContentSize <= 0) { + return combined(); + } else { + final var theContent = content.orElseThrow(); + return Optional.of(theContent.slice(stdoutContentSize, theContent.size()).data()); + } + } + + @SuppressWarnings("unchecked") + static CommandOutput empty() { + return (CommandOutput)EMPTY; + } + + private boolean withoutExtractableStdout() { + return interleaved || content.isEmpty() || stdoutContentSize < 0; + } + + private boolean withoutExtractableStderr() { + return interleaved || content.isEmpty() || stdoutContentSize > content.get().size(); + } + + private static final CommandOutput EMPTY = new CommandOutput<>(); + } + + private record ToolProviderExecutable(ToolProvider tp, List args, CommandOutputControl coc) implements Executable { + + ToolProviderExecutable { + Objects.requireNonNull(tp); + Objects.requireNonNull(args); + Objects.requireNonNull(coc); + } + + @Override + public Result execute() throws IOException, InterruptedException { + return coc.execute(tp, -1, args.toArray(String[]::new)); + } + + @Override + public Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException { + return coc.execute(tp, unit.toMillis(timeout), args.toArray(String[]::new)); + } + + @Override + public ExecutableAttributes attributes() { + return new ToolProviderAttributes(tp.name(), args); + } + } + + private record ProcessExecutable(ProcessBuilder pb, CommandOutputControl coc) implements Executable { + + ProcessExecutable { + Objects.requireNonNull(pb); + Objects.requireNonNull(coc); + } + + @Override + public Result execute() throws IOException, InterruptedException { + return coc.execute(pb, -1L); + } + + @Override + public Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException { + return coc.execute(pb, unit.toMillis(timeout)); + } + + @Override + public ExecutableAttributes attributes() { + return new ProcessAttributes(Optional.empty(), pb.command()); + } + } + + private static Optional getPID(Process p) { + try { + return Optional.of(p.pid()); + } catch (UnsupportedOperationException ex) { + return Optional.empty(); + } + } + + private static void suppressIOException(ThrowingRunnable r) { + try { + r.run(); + } catch (IOException ex) {} + } + + private static void suppressIOException(ThrowingConsumer c, T value) { + suppressIOException(() -> { + c.accept(value); + }); + } + + private int flags; + private final OutputStreamsControl outputStreamsControl; + private PrintStream dumpStdout; + private PrintStream dumpStderr; + private Charset charset; + private Consumer processListener; + + // Executor to run subprocess output stream gobblers. + // Output stream gobblers should start fetching output streams ASAP after the process starts. + // No pooling, no waiting. + // CompletableFuture#runAsync() method starts an output stream gobbler. + // If used with the default executor, it is known to make WiX3 light.exe create + // a locked msi file when multiple jpackage tool providers are executed asynchronously. + // The AsyncTest fails with cryptic java.nio.file.FileSystemException error: + // jtreg_open_test_jdk_tools_jpackage_share_AsyncTest_java\\tmp\\jdk.jpackage8108811639097525318\\msi\\Foo-1.0.msi: The process cannot access the file because it is being used by another process. + // The remedy for the problem is to use non-pooling executor to run subprocess output stream gobblers. + private final java.util.concurrent.Executor gobblerExecutor = Executors.newVirtualThreadPerTaskExecutor(); + + private enum OutputControlOption { + SAVE_ALL, SAVE_FIRST_LINE, DUMP + } + + private enum Flag { + DUMP (0x01), + REDIRECT_STDERR (0x02), + BINARY_OUTPUT (0x04), + STORE_OUTPUT_IN_FILES (0x08), + DISCARD_STDOUT (0x10), + DISCARD_STDERR (0x20), + ; + + Flag(int value) { + this.value = value; + } + + int set(int flags, boolean set) { + if (set) { + return flags | value; + } else { + return flags & ~value; + } + } + + boolean isSet(int flags) { + return (flags & value) != 0; + } + + private final int value; + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java similarity index 77% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java index 51f97ad2cd6..d9ededcbadb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.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 @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; import java.util.Optional; import java.util.function.BiConsumer; @@ -32,39 +32,43 @@ import java.util.regex.Pattern; /** * Add quotes to the given string in a configurable way. */ -final class Enquoter { +public final class Enquoter { private Enquoter() { setQuoteChar('"'); } - static Enquoter forPropertyValues() { + public static Enquoter identity() { + return new Enquoter(); + } + + public static Enquoter forPropertyValues() { return new Enquoter() .setEnquotePredicate(QUOTE_IF_WHITESPACES) .setEscaper(PREPEND_BACKSLASH); } - static Enquoter forShellLiterals() { + public static Enquoter forShellLiterals() { return forShellLiterals('\''); } - static Enquoter forShellLiterals(char quoteChar) { + public static Enquoter forShellLiterals(char quoteChar) { return new Enquoter() .setQuoteChar(quoteChar) .setEnquotePredicate(x -> true) .setEscaper(PREPEND_BACKSLASH); } - String applyTo(String v) { + public String applyTo(String v) { if (!needQuotes.test(v)) { return v; } else { var buf = new StringBuilder(); buf.appendCodePoint(beginQuoteChr); - Optional.of(escaper).ifPresentOrElse(op -> { + Optional.ofNullable(escaper).ifPresentOrElse(op -> { v.codePoints().forEachOrdered(chr -> { if (chr == beginQuoteChr || chr == endQuoteChr) { - escaper.accept(chr, buf); + op.accept(chr, buf); } else { buf.appendCodePoint(chr); } @@ -77,28 +81,23 @@ final class Enquoter { } } - Enquoter setQuoteChar(char chr) { + public Enquoter setQuoteChar(char chr) { beginQuoteChr = chr; endQuoteChr = chr; return this; } - Enquoter setEscaper(BiConsumer v) { + public Enquoter setEscaper(BiConsumer v) { escaper = v; return this; } - Enquoter setEnquotePredicate(Predicate v) { + public Enquoter setEnquotePredicate(Predicate v) { needQuotes = v; return this; } - private int beginQuoteChr; - private int endQuoteChr; - private BiConsumer escaper; - private Predicate needQuotes = str -> false; - - private static final Predicate QUOTE_IF_WHITESPACES = new Predicate() { + public static final Predicate QUOTE_IF_WHITESPACES = new Predicate() { @Override public boolean test(String t) { return pattern.matcher(t).find(); @@ -106,8 +105,13 @@ final class Enquoter { private final Pattern pattern = Pattern.compile("\\s"); }; - private static final BiConsumer PREPEND_BACKSLASH = (chr, buf) -> { + public static final BiConsumer PREPEND_BACKSLASH = (chr, buf) -> { buf.append('\\'); buf.appendCodePoint(chr); }; + + private int beginQuoteChr; + private int endQuoteChr; + private BiConsumer escaper; + private Predicate needQuotes = str -> false; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java similarity index 63% rename from src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java index 723614f9bd6..95629e7d4b5 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.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 @@ -23,54 +23,49 @@ * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.AppImageLayout; /** * An abstraction of macOS Application bundle. * * @see https://en.wikipedia.org/wiki/Bundle_(macOS)#Application_bundles */ -record MacBundle(Path root) { +public record MacBundle(Path root) { - MacBundle { + public MacBundle { Objects.requireNonNull(root); } - boolean isValid() { + public boolean isValid() { return Files.isDirectory(contentsDir()) && Files.isDirectory(macOsDir()) && Files.isRegularFile(infoPlistFile()); } - boolean isSigned() { - return Files.isDirectory(contentsDir().resolve("_CodeSignature")); - } - - Path contentsDir() { + public Path contentsDir() { return root.resolve("Contents"); } - Path homeDir() { + public Path homeDir() { return contentsDir().resolve("Home"); } - Path macOsDir() { + public Path macOsDir() { return contentsDir().resolve("MacOS"); } - Path resourcesDir() { + public Path resourcesDir() { return contentsDir().resolve("Resources"); } - Path infoPlistFile() { + public Path infoPlistFile() { return contentsDir().resolve("Info.plist"); } - static Optional fromPath(Path path) { + public static Optional fromPath(Path path) { var bundle = new MacBundle(path); if (bundle.isValid()) { return Optional.of(bundle); @@ -78,20 +73,4 @@ record MacBundle(Path root) { return Optional.empty(); } } - - static Optional fromAppImageLayout(AppImageLayout layout) { - final var root = layout.rootDirectory(); - final var bundleSubdir = root.relativize(layout.runtimeDirectory()); - final var contentsDirname = Path.of("Contents"); - var bundleRoot = root; - for (int i = 0; i != bundleSubdir.getNameCount(); i++) { - var nameComponent = bundleSubdir.getName(i); - if (contentsDirname.equals(nameComponent)) { - return Optional.of(new MacBundle(bundleRoot)); - } else { - bundleRoot = bundleRoot.resolve(nameComponent); - } - } - return Optional.empty(); - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java index 2c693939dab..4c85358d424 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.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 @@ -35,7 +35,6 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; @@ -311,7 +310,7 @@ public final class PListReader { } } - public PListReader(byte[] xmlData) throws ParserConfigurationException, SAXException, IOException { + public PListReader(byte[] xmlData) throws SAXException, IOException { this(XmlUtils.initDocumentBuilder().parse(new ByteArrayInputStream(xmlData))); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java new file mode 100644 index 00000000000..c67373e8233 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java @@ -0,0 +1,194 @@ +/* + * 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 + * 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.jpackage.internal.util; + +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + +import java.time.Duration; +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.function.ThrowingSupplier; + +public class RetryExecutor { + + public RetryExecutor(Class exceptionType) { + this.exceptionType = Objects.requireNonNull(exceptionType); + setMaxAttemptsCount(5); + setAttemptTimeout(2, TimeUnit.SECONDS); + } + + final public Class exceptionType() { + return exceptionType; + } + + public RetryExecutor setExecutable(ThrowingFunction>, T, E> v) { + executable = v; + return this; + } + + final public RetryExecutor setExecutable(ThrowingSupplier v) { + if (v != null) { + setExecutable(_ -> { + return v.get(); + }); + } else { + executable = null; + } + return this; + } + + public RetryExecutor setMaxAttemptsCount(int v) { + attempts = v; + return this; + } + + final public RetryExecutor setAttemptTimeout(long v, TimeUnit unit) { + return setAttemptTimeout(Duration.of(v, unit.toChronoUnit())); + } + + public RetryExecutor setAttemptTimeout(Duration v) { + timeout = v; + return this; + } + + public RetryExecutor setExceptionMapper(Function v) { + toUnchecked = v; + return this; + } + + public RetryExecutor setSleepFunction(Consumer v) { + sleepFunction = v; + return this; + } + + final public RetryExecutor mutate(Consumer> mutator) { + mutator.accept(this); + return this; + } + + public T execute() throws E { + var curExecutable = executable(); + T result = null; + var attemptIter = new DefaultContext(); + while (attemptIter.hasNext()) { + attemptIter.next(); + try { + result = curExecutable.apply(attemptIter); + break; + } catch (Exception ex) { + if (!exceptionType.isInstance(ex)) { + throw ExceptionBox.toUnchecked(ex); + } else if (attemptIter.isLastAttempt()) { + // No more attempts left. This is fatal. + throw exceptionType.cast(ex); + } else { + curExecutable = executable(); + } + } + + sleep(); + } + + return result; + } + + final public T executeUnchecked() { + try { + return execute(); + } catch (Error | RuntimeException t) { + throw t; + } catch (Exception ex) { + if (exceptionType.isInstance(ex)) { + throw Optional.ofNullable(toUnchecked).orElse(ExceptionBox::toUnchecked).apply(exceptionType.cast(ex)); + } else { + // Unreachable unless it is a direct subclass of Throwable, + // which is not Error or Exception which should not happen. + throw ExceptionBox.reachedUnreachable(); + } + } + } + + public interface Context { + boolean isLastAttempt(); + int attempt(); + T executor(); + } + + private final class DefaultContext implements Context>, Iterator { + + @Override + public boolean isLastAttempt() { + return !hasNext(); + } + + @Override + public int attempt() { + return attempt; + } + + @Override + public boolean hasNext() { + return (attempts - attempt) > 1; + } + + @Override + public Void next() { + attempt++; + return null; + } + + @Override + public RetryExecutor executor() { + return RetryExecutor.this; + } + + private int attempt = -1; + } + + private ThrowingFunction>, T, E> executable() { + return Optional.ofNullable(executable).orElseThrow(() -> { + return new IllegalStateException("No executable"); + }); + } + + private void sleep() { + Optional.ofNullable(timeout).ifPresent(Optional.ofNullable(sleepFunction).orElseGet(() -> { + return toConsumer(Thread::sleep); + })); + } + + private final Class exceptionType; + private ThrowingFunction>, T, E> executable; + private int attempts; + private Duration timeout; + private Function toUnchecked; + private Consumer sleepFunction; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java new file mode 100644 index 00000000000..db098eb2b48 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java @@ -0,0 +1,89 @@ +/* + * 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.jpackage.internal.util; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Objects; +import jdk.jpackage.internal.util.function.ThrowingConsumer; + +public final class TeeOutputStream extends OutputStream { + + public TeeOutputStream(Iterable items) { + items.forEach(Objects::requireNonNull); + this.items = items; + } + + @Override + public void write(int b) throws IOException { + for (final var item : items) { + item.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + for (final var item : items) { + item.write(b); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + for (final var item : items) { + item.write(b, off, len); + } + } + + @Override + public void flush() throws IOException { + forEach(Flushable::flush); + } + + @Override + public void close() throws IOException { + forEach(Closeable::close); + } + + private void forEach(ThrowingConsumer c) throws IOException { + IOException firstEx = null; + for (final var item : items) { + try { + c.accept(item); + } catch (IOException e) { + if (firstEx == null) { + firstEx = e; + } + } + } + if (firstEx != null) { + throw firstEx; + } + } + + private final Iterable items; +} diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java index e1e02ba7850..b196a5805a0 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.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 @@ -24,8 +24,6 @@ */ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.Application; import java.io.IOException; import java.nio.file.Path; import java.util.Collections; @@ -36,6 +34,9 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.util.Enquoter; /** * Helper to install launchers as services for Unix installers. diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java index dc9891f154a..de52a222d7d 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.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 @@ -31,16 +31,17 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_EXE import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_MSI; import jdk.jpackage.internal.cli.Options; -import jdk.jpackage.internal.util.Result; public class WinBundlingEnvironment extends DefaultBundlingEnvironment { public WinBundlingEnvironment() { - super(build() - .defaultOperation(CREATE_WIN_EXE) - .bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage) - .bundler(CREATE_WIN_EXE, LazyLoad::sysEnv, WinBundlingEnvironment::createExePackage) - .bundler(CREATE_WIN_MSI, LazyLoad::sysEnv, WinBundlingEnvironment::createMsiPackage)); + super(build().mutate(builder -> { + var sysEnv = runOnce(WinSystemEnvironment::create); + + builder + .bundler(CREATE_WIN_EXE, sysEnv, WinBundlingEnvironment::createExePackage) + .bundler(CREATE_WIN_MSI, sysEnv, WinBundlingEnvironment::createMsiPackage); + }).defaultOperation(CREATE_WIN_EXE).bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage)); } private static void createMsiPackage(Options options, WinSystemEnvironment sysEnv) { @@ -98,12 +99,4 @@ public class WinBundlingEnvironment extends DefaultBundlingEnvironment { } } - private static final class LazyLoad { - - static Result sysEnv() { - return SYS_ENV; - } - - private static final Result SYS_ENV = WinSystemEnvironment.create(); - } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java index 9a13a0f954d..fd86331e2f1 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.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 @@ -79,8 +79,6 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat private void wrapMsiInExe() throws IOException { - Log.verbose(I18N.format("message.outputting-to-location", outputDir.toAbsolutePath())); - final var msi = msi(); // Copy template msi wrapper next to msi file @@ -102,7 +100,5 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat Files.copy(exePath, dstExePath, StandardCopyOption.REPLACE_EXISTING); dstExePath.toFile().setExecutable(true); - - Log.verbose(I18N.format("message.output-location", outputDir.toAbsolutePath())); } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java index 915d034bd82..c52be726fd2 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.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 @@ -315,7 +315,6 @@ final class WinMsiPackager implements Consumer { private void buildPackage() throws IOException { final var msiOut = outputDir.resolve(pkg.packageFileNameWithSuffix()); - Log.verbose(I18N.format("message.generating-msi", msiOut.toAbsolutePath())); wixPipeline.buildMsi(msiOut.toAbsolutePath()); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java index e2ed175fbf3..c4f8610312a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -233,10 +233,10 @@ public enum WixTool { // Detect FIPS mode var fips = false; try { - final var exec = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true); - final var exitCode = exec.execute(); + final var result = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true).execute(); + final var exitCode = result.getExitCode(); if (exitCode != 0 /* 308 */) { - final var output = exec.getOutput(); + final var output = result.getOutput(); if (!output.isEmpty() && output.get(0).contains("error CNDL0308")) { fips = true; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 1f485e6c6c8..38d0bd02bbb 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.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 @@ -56,13 +56,9 @@ error.missing-service-installer.advice=Add 'service-installer.exe' service insta message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.outputting-to-location=Generating EXE for installer to: {0}. -message.output-location=Installer (.exe) saved to: {0} message.tool-version=Detected [{0}] version [{1}]. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.product-code=MSI ProductCode: {0}. message.upgrade-code=MSI UpgradeCode: {0}. message.preparing-msi-config=Preparing MSI config: {0}. -message.generating-msi=Generating MSI: {0}. - diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java index d8fd13fdd7c..af28135e673 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,50 @@ package jdk.test.failurehandler; +import java.io.FileWriter; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; public class HtmlPage implements AutoCloseable { + static final String STYLE_SHEET_FILENAME = "failure-handler-style.css"; + static final String SCRIPT_FILENAME = "failure-handler-script.js"; + private final PrintWriter writer; private final HtmlSection rootSection; - public HtmlPage(PrintWriter writer) { - Objects.requireNonNull(writer, "writer cannot be null"); - this.writer = writer; + /** + * Constructs a {@code HtmlPage} + * + * @param dir The directory into which the HTML file and related resources will be created + * @param htmlFileName The HTML file name + * @param append if {@code true} then the content will be appended to the file represented + * by the {@code htmlFileName}, else the {@code htmlFileName} will be overwritten + * with the new content + * @throws IllegalArgumentException if {@code dir} is not a directory or if the + * {@code htmlFileName} is {@linkplain String#isBlank() blank} + * @throws IOException if there is an error constructing file resource(s) for this HTML page + */ + public HtmlPage(final Path dir, final String htmlFileName, final boolean append) + throws IOException { + Objects.requireNonNull(dir, "directory cannot be null"); + Objects.requireNonNull(htmlFileName, "HTML file name cannot be null"); + if (!Files.isDirectory(dir)) { + throw new IllegalArgumentException(dir + " is not a directory"); + } + if (htmlFileName.isBlank()) { + throw new IllegalArgumentException("HTML file name cannot be blank"); + } + final FileWriter fileWriter = new FileWriter(dir.resolve(htmlFileName).toFile(), append); + this.writer = new PrintWriter(fileWriter, true); + createScriptFile(dir); + createStyleSheetFile(dir); rootSection = new HtmlSection(writer); } + @Override public void close() { writer.close(); @@ -44,4 +75,71 @@ public class HtmlPage implements AutoCloseable { public HtmlSection getRootSection() { return rootSection; } + + private static void createStyleSheetFile(final Path destDir) throws IOException { + final Path styleSheet = destDir.resolve(STYLE_SHEET_FILENAME); + if (Files.exists(styleSheet)) { + return; + } + final String content = """ + div { display:none;} + """; + Files.writeString(styleSheet, content); + } + + private static void createScriptFile(final Path destDir) throws IOException { + final Path script = destDir.resolve(SCRIPT_FILENAME); + if (Files.exists(script)) { + return; + } + final String content = """ + function doShow(e) { + while (e != null) { + if (e.tagName == 'DIV') { + e.style.display = 'block'; + } + e = e.parentNode; + } + } + + function showHandler(event) { + elementId = this.dataset.show; + elementToShow = document.getElementById(elementId); + doShow(elementToShow); + } + + function toggleHandler(event) { + toggleElementId = this.dataset.toggle; + elementToToggle = document.getElementById(toggleElementId); + d = elementToToggle.style.display; + if (d == 'block') { + elementToToggle.style.display = 'none'; + } else { + doShow(elementToToggle); + } + } + + function bodyLoadHandler() { + const index = location.href.indexOf("#"); + if (index != -1) { + doShow(document.getElementById(location.href.substring(index + 1))); + } + // elements that require the "toggleHandler" function to be registered + // as an event handler for the onclick event + const requiringToggleHandler = document.querySelectorAll("[data-toggle]"); + for (const e of requiringToggleHandler) { + e.addEventListener("click", toggleHandler); + } + // elements that require the "showHandler" function to be registered + // as an event handler for the onclick event + const requiringShowHandler = document.querySelectorAll("[data-show]"); + for (const e of requiringShowHandler) { + e.addEventListener("click", showHandler); + } + } + // register a onload event handler + window.addEventListener("DOMContentLoaded", bodyLoadHandler); + """; + Files.writeString(script, content); + } } diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java index 141caf450bd..53ffab95a1e 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.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 @@ -57,41 +57,15 @@ public class HtmlSection { if (rootSection == null) { this.rootSection = this; this.pw.println(""); - this.pw.println("\n" - + "\n" - + "\n" - + ""); - this.pw.println(""); + this.pw.println(""); + this.pw.println( + ""); + this.pw.println( + ""); + this.pw.println(""); + + this.pw.println(""); } else { this.rootSection = rootSection; this.pw.print("

    "); @@ -146,7 +120,7 @@ public class HtmlSection { } else if (child != null) { path = String.format("%s.%s", path, child); } - pw.printf("%2$s%n", + pw.printf("%2$s%n", path, name); } @@ -188,7 +162,7 @@ public class HtmlSection { : String.format("%s.%s", parent.id, name), name, rootSection); this.parent = parent; - pw.printf("
  • %2$s
    ",
    +            pw.printf("
  • %2$s
    ",
                         id, name);
             }
     
    diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
    index 96c5f4f2858..32dbedb5159 100644
    --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
    +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2015, 2021, 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,13 +29,14 @@ import com.sun.javatest.TestResult;
     import com.sun.javatest.regtest.config.RegressionParameters;
     import jdk.test.failurehandler.*;
     
    -import java.io.File;
     import java.io.FileWriter;
     import java.io.IOException;
     import java.io.PrintWriter;
     import java.nio.file.Files;
     import java.nio.file.Path;
     import java.nio.file.Paths;
    +import java.util.List;
    +import java.util.stream.Stream;
     
     /**
      * The jtreg test execution observer, which gathers info about
    @@ -85,11 +86,15 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
                         testJdk, compileJdk);
                 gatherEnvInfo(workDir, name, log,
                         gathererFactory.getEnvironmentInfoGatherer());
    -            Files.walk(workDir)
    -                    .filter(Files::isRegularFile)
    -                    .filter(f -> (f.getFileName().toString().contains("core") || f.getFileName().toString().contains("mdmp")))
    -                    .forEach(core -> gatherCoreInfo(workDir, name,
    -                            core, log, gathererFactory.getCoreInfoGatherer()));
    +            // generate a cores.html file after parsing the core dump files (if any)
    +            List coreFiles;
    +            try (Stream paths = Files.walk(workDir)) {
    +                coreFiles = paths.filter(Files::isRegularFile)
    +                        .filter(f -> (f.getFileName().toString().contains("core")
    +                                || f.getFileName().toString().contains("mdmp")))
    +                        .toList();
    +            }
    +            gatherCoreInfo(workDir, name, coreFiles, log, gathererFactory.getCoreInfoGatherer());
             } catch (Throwable e) {
                 log.printf("ERROR: exception in observer %s:", name);
                 e.printStackTrace(log);
    @@ -103,27 +108,29 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
             }
         }
     
    -    private void gatherCoreInfo(Path workDir, String name, Path core, PrintWriter log,
    -                               CoreInfoGatherer gatherer) {
    -        File output = workDir.resolve(CORES_OUTPUT).toFile();
    -        try (HtmlPage html = new HtmlPage(new PrintWriter(
    -                new FileWriter(output, true), true))) {
    +    private void gatherCoreInfo(Path workDir, String name, List coreFiles,
    +                                PrintWriter log, CoreInfoGatherer gatherer) {
    +        if (coreFiles.isEmpty()) {
    +            return;
    +        }
    +        try (HtmlPage html = new HtmlPage(workDir, CORES_OUTPUT, true)) {
                 try (ElapsedTimePrinter timePrinter
                              = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
    -                gatherer.gatherCoreInfo(html.getRootSection(), core);
    +                // gather information from the contents of each core file
    +                for (Path coreFile : coreFiles) {
    +                    gatherer.gatherCoreInfo(html.getRootSection(), coreFile);
    +                }
                 }
             } catch (Throwable e) {
    -            log.printf("ERROR: exception in observer on getting environment "
    -                    + "information %s:", name);
    +            log.printf("ERROR: exception in %s observer while gathering information from"
    +                    + " core dump file", name);
                 e.printStackTrace(log);
             }
         }
     
         private void gatherEnvInfo(Path workDir, String name, PrintWriter log,
                                    EnvironmentInfoGatherer gatherer) {
    -        File output = workDir.resolve(ENVIRONMENT_OUTPUT).toFile();
    -        try (HtmlPage html = new HtmlPage(new PrintWriter(
    -                new FileWriter(output, true), true))) {
    +        try (HtmlPage html = new HtmlPage(workDir, ENVIRONMENT_OUTPUT, true)) {
                 try (ElapsedTimePrinter timePrinter
                              = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
                     gatherer.gatherEnvironmentInfo(html.getRootSection());
    diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
    index 63ed7e5f2c7..c1f1ddfb69f 100644
    --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
    +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.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
    @@ -69,16 +69,7 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
             }
             try {
                 actionsLog.printf("%s ---%n", name);
    -
    -            File output = workDir.resolve(OUTPUT_FILENAME).toFile();
    -            try {
    -                PrintWriter pw = new PrintWriter(new FileWriter(output, true), true);
    -                runGatherer(name, workDir, actionsLog, pw, pid);
    -            } catch (IOException e) {
    -                actionsLog.printf("IOException: cannot open output file[%s] : %s",
    -                        output, e.getMessage());
    -                e.printStackTrace(actionsLog);
    -            }
    +            runGatherer(name, actionsLog, pid);
             } finally {
                 actionsLog.printf("--- %s%n", name);
                 // don't close jtreg log
    @@ -90,9 +81,9 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
             }
         }
     
    -    private void runGatherer(String name, Path workDir, PrintWriter log,
    -                             PrintWriter out, long pid) {
    -        try (HtmlPage html = new HtmlPage(out)) {
    +    private void runGatherer(String name, PrintWriter log, long pid) {
    +        Path workDir = outputDir.toPath();
    +        try (HtmlPage html = new HtmlPage(workDir, OUTPUT_FILENAME, true)) {
                 ProcessInfoGatherer gatherer = new GathererFactory(
                         OS.current().family,
                         workDir, log, testJdk.toPath()).getProcessInfoGatherer();
    diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp
    index 61a9ff7fb70..42767e9fcab 100644
    --- a/test/hotspot/gtest/opto/test_rangeinference.cpp
    +++ b/test/hotspot/gtest/opto/test_rangeinference.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
    @@ -202,7 +202,7 @@ static void test_canonicalize_constraints_random() {
       }
     }
     
    -TEST(opto, canonicalize_constraints) {
    +TEST_VM(opto, canonicalize_constraints) {
       test_canonicalize_constraints_trivial();
       test_canonicalize_constraints_exhaustive, uintn_t<1>>();
       test_canonicalize_constraints_exhaustive, uintn_t<2>>();
    diff --git a/test/hotspot/gtest/runtime/test_atomic.cpp b/test/hotspot/gtest/runtime/test_atomic.cpp
    index b37c14d41a7..753dde0ca57 100644
    --- a/test/hotspot/gtest/runtime/test_atomic.cpp
    +++ b/test/hotspot/gtest/runtime/test_atomic.cpp
    @@ -162,6 +162,35 @@ TEST_VM(AtomicIntegerTest, cmpxchg_int64) {
       Support().test();
     }
     
    +template
    +struct AtomicIntegerCmpsetTestSupport {
    +  Atomic _test_value;
    +
    +  AtomicIntegerCmpsetTestSupport() : _test_value{} {}
    +
    +  void test() {
    +    T zero = 0;
    +    T five = 5;
    +    T ten = 10;
    +    _test_value.store_relaxed(zero);
    +    EXPECT_FALSE(_test_value.compare_set(five, ten));
    +    EXPECT_EQ(zero, _test_value.load_relaxed());
    +    EXPECT_TRUE(_test_value.compare_set(zero, ten));
    +    EXPECT_EQ(ten, _test_value.load_relaxed());
    +  }
    +};
    +
    +TEST_VM(AtomicIntegerTest, cmpset_int32) {
    +  using Support = AtomicIntegerCmpsetTestSupport;
    +  Support().test();
    +}
    +
    +TEST_VM(AtomicIntegerTest, cmpset_int64) {
    +  // Check if 64-bit atomics are available on the machine.
    +  using Support = AtomicIntegerCmpsetTestSupport;
    +  Support().test();
    +}
    +
     struct AtomicXchgAndCmpxchg1ByteStressSupport {
       char _default_val;
       int  _base;
    diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
    index 4e16e328ab4..13e1ea30a34 100644
    --- a/test/hotspot/jtreg/ProblemList.txt
    +++ b/test/hotspot/jtreg/ProblemList.txt
    @@ -77,8 +77,6 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586
     
     compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64
     
    -serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150 generic-all
    -
     #############################################################################
     
     # :hotspot_gc
    diff --git a/test/hotspot/jtreg/compiler/c2/ClearArray.java b/test/hotspot/jtreg/compiler/c2/ClearArray.java
    index ee376641650..d218eef5780 100644
    --- a/test/hotspot/jtreg/compiler/c2/ClearArray.java
    +++ b/test/hotspot/jtreg/compiler/c2/ClearArray.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 ClearArray.java
    - * @bug 8284883
    + * @bug 8284883 8374570
      * @compile ClearArray.java
      * @summary ClearArray instruction overflows scratch buffer
      * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -Xbatch
    @@ -33,6 +33,8 @@
      *   -XX:InitArrayShortSize=32768 -XX:-IdealizeClearArrayNode -XX:UseAVX=3 compiler.c2.ClearArray
      * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -Xbatch
      *   -XX:InitArrayShortSize=32768 -XX:MaxVectorSize=8 -XX:-IdealizeClearArrayNode -XX:UseAVX=3 compiler.c2.ClearArray
    + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -Xbatch
    + *   -XX:+EnableX86ECoreOpts -XX:MaxVectorSize=8 -XX:UseAVX=3 compiler.c2.ClearArray
      */
     
     package compiler.c2;
    diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java b/test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java
    new file mode 100644
    index 00000000000..4d70ee92a92
    --- /dev/null
    +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java
    @@ -0,0 +1,56 @@
    +/*
    + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.
    + *
    + * 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.c2.igvn;
    +
    +/*
    + * @test
    + * @bug 8372302
    + * @summary ModINode::Ideal and ModLNode::Ideal use an intermediate "hook" node
    + *          to keep stuff alive between phase->transform(...) calls. In some cases,
    + *          this node is not properly deleted before returning, causing failure
    + *          in the verification because the node count has changed. This test
    + *          ensures that the intermediate node gets destroyed before returning.
    + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
    + *      -Xcomp -XX:-TieredCompilation
    + *      -XX:CompileCommand=compileonly,${test.main.class}::test*
    + *      -XX:VerifyIterativeGVN=1110
    + *      ${test.main.class}
    + * @run main ${test.main.class}
    + *
    + */
    +
    +public class TestModIdealCreatesUselessNode {
    +    static int test0(int x) {
    +        return x % Integer.MIN_VALUE;
    +    }
    +
    +    static long test1(long x) {
    +        return x % Long.MIN_VALUE;
    +    }
    +
    +    public static void main(String[] args) {
    +        test0(0);
    +        test1(0L);
    +    }
    +}
    diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.java
    new file mode 100644
    index 00000000000..1221c1b8064
    --- /dev/null
    +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.java
    @@ -0,0 +1,58 @@
    +/*
    + * 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.escapeAnalysis;
    +
    +import java.util.Objects;
    +
    +/*
    + * @test
    + * @bug 8374435
    + * @summary assert during escape analysis when splitting a Load through a Phi because the input of
    + *          the result Phi is a Load not from an AddP
    + * @run main/othervm -XX:-UseOnStackReplacement -XX:-UseCompressedOops ${test.main.class}
    + */
    +public class TestSplitLoadThroughPhiDuringEA {
    +    static class Holder {
    +        Object o;
    +    }
    +
    +    public static void main(String[] args) {
    +        Object o = new Object();
    +        Holder h = new Holder();
    +        for (int i = 0; i < 20000; i++) {
    +            test(true, h, o);
    +            test(false, h, o);
    +        }
    +    }
    +
    +    private static Object test(boolean b, Holder h, Object o) {
    +        h = Objects.requireNonNull(h);
    +        if (b) {
    +            h = new Holder();
    +            // This access has the pattern LoadP -> LoadP, which upsets the compiler because the
    +            // pointer input of a LoadP is not an AddP
    +            h.o = o.getClass();
    +        }
    +        return h.o;
    +    }
    +}
    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 cb0e94c9fe8..1fe05cc2b6c 100644
    --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java
    +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java
    @@ -274,6 +274,7 @@ public final class Operations {
             ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")"));
     
             // TODO: Math and other classes.
    +        // Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset.
     
             // Make sure the list is not modifiable.
             return List.copyOf(ops);
    @@ -294,7 +295,8 @@ public final class Operations {
             ops.add(Expression.make(INTS, "Float16.compare(", FLOAT16, ",", FLOAT16, ")"));
             addComparisonOperations(ops, "Float16.compare", FLOAT16);
             ops.add(Expression.make(INTS, "(", FLOAT16, ").compareTo(",  FLOAT16, ")"));
    -        ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")"));
    +        // Note: There are NaN encodings with bit set or unset.
    +        ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")", WITH_NONDETERMINISTIC_RESULT));
             ops.add(Expression.make(FLOAT16, "Float16.divide(", FLOAT16, ",", FLOAT16, ")"));
             ops.add(Expression.make(BOOLEANS, "", FLOAT16, ".equals(", FLOAT16, ")"));
             // Note: there are multiple NaN values with different bit representations.
    diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java
    index 5d20ce659b9..825d5ca60bc 100644
    --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java
    +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.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
    @@ -1052,35 +1052,20 @@ public class TestAliasingFuzzer {
                         case Aliasing.CONTAINER_DIFFERENT,
                              Aliasing.CONTAINER_SAME_ALIASING_NEVER,
                              Aliasing.CONTAINER_UNKNOWN_ALIASING_NEVER ->
    -                        // We would have liked to check that there is no multiversioning.
    -                        //
    -                        // But sadly there are some cases that have issues with RCE and/or
    -                        // predicates, and so we end up using multiversioning anyway. We
    -                        // should fix those cases eventually, to strengthen the checks here.
    -                        //
    -                        // The array cases are a little more tame, and do not have the same
    -                        // issues as the MemorySegment cases.
    -                        (containerKind == ContainerKind.ARRAY)
    -                        ? """
    -                          // Aliasing check should never fail at runtime, so the predicate
    -                          // should never fail, and we do not have to use multiversioning.
    -                          // Failure could have a few causes:
    -                          // - issues with doing RCE / missing predicates
    -                          //   -> other loop-opts need to be fixed
    -                          // - predicate fails: recompile with multiversioning
    -                          //   -> logic in runtime check may be wrong
    -                          @IR(counts = {".*multiversion.*", "= 0"},
    -                              phase = CompilePhase.PRINT_IDEAL,
    -                              applyIf = {"UseAutoVectorizationPredicate", "true"},
    -                              applyIfPlatform = {"64-bit", "true"},
    -                              applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"})
    -                          """
    -                        : """
    -                          // Due to cases like JDK-8360204 and JDK-8365982, there can be issues
    -                          // with RCE leading cases where we remove predicates and then unroll again
    -                          // and then end up multiversioning. These cases seem relatively rare but
    -                          // prevent us from asserting that there is never multiversioning in these cases.
    -                          """;
    +                            """
    +                            // Aliasing check should never fail at runtime, so the predicate
    +                            // should never fail, and we do not have to use multiversioning.
    +                            // Failure could have a few causes:
    +                            // - issues with doing RCE / missing predicates
    +                            //   -> other loop-opts need to be fixed
    +                            // - predicate fails: recompile with multiversioning
    +                            //   -> logic in runtime check may be wrong
    +                            @IR(counts = {".*multiversion.*", "= 0"},
    +                                phase = CompilePhase.PRINT_IDEAL,
    +                                applyIf = {"UseAutoVectorizationPredicate", "true"},
    +                                applyIfPlatform = {"64-bit", "true"},
    +                                applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"})
    +                            """;
                        case Aliasing.CONTAINER_SAME_ALIASING_UNKNOWN,
                              Aliasing.CONTAINER_UNKNOWN_ALIASING_UNKNOWN ->
                                 """
    diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java
    index 26a49aba7bf..53c1b89a203 100644
    --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java
    +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.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
    @@ -479,6 +479,32 @@ public class TestSubwordTruncation {
             return new Object[] { in, res };
         }
     
    +    @Test
    +    @IR(counts = { IRNode.CAST_II, ">0" })
    +    @Warmup(0)
    +    public Object[] testCastII() {
    +        byte[] bytes = new byte[400];
    +        intField = 6;
    +        int i = 0;
    +        int j = 1;
    +
    +        do {
    +            bytes[j] = (byte) i;
    +            int k = 1;
    +
    +            do {
    +                i <<= intField;
    +                i += (k ^ i);
    +                i -= j;
    +
    +                for (int u = 1; 1 > u; u++) {
    +                }
    +            } while (++k < 8);
    +        } while (++j < 191);
    +
    +        return new Object[] { bytes };
    +    }
    +
         public static void main(String[] args) {
             TestFramework.run();
         }
    diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java
    index bbf08c0dae7..986cd044737 100644
    --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java
    +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.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
    @@ -88,6 +88,11 @@ public class ShareTmpDir {
                 };
             t1.start();
     
    +        while (!started1.exists()) {
    +            System.out.println("Waiting for first JVM to start");
    +            Thread.sleep(1000);
    +        }
    +
             Thread t2 = new Thread() {
                     public void run() {
                         synchronized(lock) {
    @@ -98,8 +103,8 @@ public class ShareTmpDir {
                 };
             t2.start();
     
    -        while (!started1.exists() || !started2.exists()) {
    -            System.out.println("Waiting for all two JVMs to start");
    +        while (!started2.exists()) {
    +            System.out.println("Waiting for second JVM to start");
                 Thread.sleep(1000);
             }
     
    diff --git a/test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java b/test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java
    new file mode 100644
    index 00000000000..353daaef8b6
    --- /dev/null
    +++ b/test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java
    @@ -0,0 +1,76 @@
    +/*
    + * 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.
    + */
    +
    +package gc.epsilon;
    +
    +/**
    + * @test TestInitAllocs
    + * @requires vm.gc.Epsilon
    + * @summary Test that allocation path taken in early JVM phases works
    + *
    + * @run main/othervm -Xmx256m
    + *                   -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
    + *                   gc.epsilon.TestInitAllocs
    + *
    + * @run main/othervm -Xmx256m
    + *                   -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
    + *                   -XX:+UseTLAB
    + *                   -XX:+UseCompressedOops
    + *                   -XX:EpsilonMinHeapExpand=1024
    + *                   -XX:EpsilonUpdateCountersStep=1
    + *                   -XX:EpsilonPrintHeapSteps=1000000
    + *                   gc.epsilon.TestInitAllocs
    + *
    + * @run main/othervm -Xmx256m
    + *                   -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
    + *                   -XX:+UseTLAB
    + *                   -XX:-UseCompressedOops
    + *                   -XX:EpsilonMinHeapExpand=1024
    + *                   -XX:EpsilonUpdateCountersStep=1
    + *                   -XX:EpsilonPrintHeapSteps=1000000
    + *                   gc.epsilon.TestInitAllocs
    + *
    + * @run main/othervm -Xmx256m
    + *                   -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
    + *                   -XX:-UseTLAB
    + *                   -XX:+UseCompressedOops
    + *                   -XX:EpsilonMinHeapExpand=1024
    + *                   -XX:EpsilonUpdateCountersStep=1
    + *                   -XX:EpsilonPrintHeapSteps=1000000
    + *                   gc.epsilon.TestInitAllocs
    + *
    + * @run main/othervm -Xmx256m
    + *                   -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
    + *                   -XX:-UseTLAB
    + *                   -XX:-UseCompressedOops
    + *                   -XX:EpsilonMinHeapExpand=1024
    + *                   -XX:EpsilonUpdateCountersStep=1
    + *                   -XX:EpsilonPrintHeapSteps=1000000
    + *                   gc.epsilon.TestInitAllocs
    + */
    +
    +public class TestInitAllocs {
    +  public static void main(String[] args) throws Exception {
    +    System.out.println("Hello World");
    +  }
    +}
    diff --git a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java
    index 78a508343d6..d87ca25fc3c 100644
    --- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java
    +++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java
    @@ -116,7 +116,7 @@ public abstract class JVMOption {
                 default:
                     throw new Error("Expected only \"int\", \"intx\", \"size_t\", "
                             + "\"uint\", \"uintx\", \"uint64_t\", or \"double\" "
    -                        + "option types! Got " + type + " type!");
    +                        + "option types! Got " + type + " type for option " + name + "!");
             }
     
             return parameter;
    diff --git a/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java b/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java
    index acff3191300..8b07cd86a72 100644
    --- a/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java
    +++ b/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.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.
      * Copyright (c) 2024, Red Hat, Inc. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
    @@ -55,7 +55,7 @@
      * @requires vm.cds.default.archive.available
      * @requires vm.cds.write.archived.java.heap
      * @requires vm.bits == 64
    - * @requires !vm.gc.Z
    + * @requires vm.gc != "Z"
      * @library /test/lib
      * @modules java.base/jdk.internal.misc
      *          java.management
    @@ -69,7 +69,7 @@
      * @requires vm.cds.default.archive.available
      * @requires vm.cds.write.archived.java.heap
      * @requires vm.bits == 64
    - * @requires !vm.gc.Z
    + * @requires vm.gc != "Z"
      * @library /test/lib
      * @modules java.base/jdk.internal.misc
      *          java.management
    diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java
    new file mode 100644
    index 00000000000..8014ed685ee
    --- /dev/null
    +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java
    @@ -0,0 +1,60 @@
    +/*
    + * 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
    + * 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 8374639
    + * @requires vm.cds.supports.aot.class.linking
    + * @library /test/lib
    + * @build DynamicDumpWithAOTLinkedStaticArchive jdk.test.whitebox.WhiteBox
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TestApp
    + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicDumpWithAOTLinkedStaticArchive
    + */
    +
    +import jdk.test.lib.cds.SimpleCDSAppTester;
    +import jdk.test.lib.process.OutputAnalyzer;
    +
    +public class DynamicDumpWithAOTLinkedStaticArchive {
    +    public static void main(String... args) throws Exception {
    +        SimpleCDSAppTester.of("DynamicDumpWithAOTLinkedStaticArchive")
    +            .classpath("app.jar")
    +            .appCommandLine("TestApp")
    +            .setGenerateBaseArchive(true)
    +            .setBaseArchiveOptions("-XX:+AOTClassLinking")
    +            .setProductionChecker((OutputAnalyzer out) -> {
    +                    out.shouldContain("HelloWorld");
    +                })
    +            .runDynamicWorkflow();
    +    }
    +}
    +
    +class TestApp {
    +    public static void main(String[] args) {
    +        System.out.println("HelloWorld");
    +        System[][][] x = new System[0][0][0];
    +        System.out.println(x);
    +    }
    +}
    diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java
    deleted file mode 100644
    index e476382c8c9..00000000000
    --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java
    +++ /dev/null
    @@ -1,170 +0,0 @@
    -/*
    - * Copyright (c) 2019, 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
    - * @summary handling of the existence of InstanceKlass::array_klasses()
    - * @requires vm.cds
    - * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
    - *          /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes
    - * @build ArrayKlassesApp
    - * @build jdk.test.whitebox.WhiteBox
    - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar ArrayKlasses.jar ArrayKlassesApp
    - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ArrayKlasses
    - */
    -
    -import jdk.test.lib.helpers.ClassFileInstaller;
    -
    -public class ArrayKlasses extends DynamicArchiveTestBase {
    -    public static void main(String[] args) throws Exception {
    -        runTest(ArrayKlasses::test);
    -    }
    -
    -    static void test() throws Exception {
    -        String topArchiveName = getNewArchiveName();
    -        final String appJar = ClassFileInstaller.getJarPath("ArrayKlasses.jar");
    -        final String mainClass = "ArrayKlassesApp";
    -        final String runtimeLogOptions =
    -            "-Xlog:class+load,class+load+array=debug,cds+dynamic=debug,cds=debug,aot+unshareable=trace";
    -
    -        // Case 1
    -        // Create a dynamic archive with the ArrayKlassesApp app class and its
    -        // array classes.
    -        dump2(null, topArchiveName,
    -              "-Xlog:cds+dynamic=debug,cds+class=debug",
    -              "-cp", appJar, mainClass)
    -              .assertNormalExit(output -> {
    -                     output.shouldMatch("cds.class.*klasses.*array \\[LArrayKlassesApp;")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[LArrayKlassesApp;")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[\\[LArrayKlassesApp;");
    -                 });
    -
    -        // Case 1
    -        // At runtime , the ArrayKlasesApp and its array class should be loaded
    -        // from the dynamic archive.
    -        run2(null, topArchiveName, runtimeLogOptions,
    -             "-cp", appJar, mainClass)
    -             .assertNormalExit(output -> {
    -                     output.shouldContain("ArrayKlassesApp source: shared objects file (top)")
    -                           .shouldContain("restore: ArrayKlassesApp with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader")
    -                           .shouldContain("[LArrayKlassesApp; source: shared objects file (top)")
    -                           .shouldContain("restore: [LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader")
    -                           .shouldContain("[[LArrayKlassesApp; source: shared objects file (top)")
    -                           .shouldContain("restore: [[LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader")
    -                           .shouldContain("[[[LArrayKlassesApp; source: shared objects file (top)")
    -                           .shouldContain("restore: [[[LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader")
    -                           .shouldHaveExitValue(0);
    -                 });
    -
    -        // Case 2
    -        // Create a dynamic archive with the array classes of java/util/Date which
    -        // is in the default CDS archive.
    -        topArchiveName = getNewArchiveName();
    -        dump2(null, topArchiveName,
    -              "-Xlog:class+load,cds+dynamic=debug,cds+class=debug",
    -              "-cp", appJar, mainClass, "system")
    -              .assertNormalExit(output -> {
    -                     output.shouldContain("java.util.Date source: shared objects file")
    -                           .shouldMatch("cds.class.*klasses.*array \\[Ljava.util.Date;")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[Ljava.util.Date;")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[\\[Ljava.util.Date;");
    -                 });
    -
    -        // Case 2
    -        // At runtime, the java/util/Date class should be loaded from the default
    -        // CDS archive; its array class should be loaded from the dynamic archive.
    -        run2(null, topArchiveName, runtimeLogOptions,
    -             "-cp", appJar, mainClass, "system")
    -             .assertNormalExit(output -> {
    -                     output.shouldContain("java.util.Date source: shared objects file")
    -                           .shouldContain("restore: java.util.Date with class loader: boot")
    -                           .shouldContain("[Ljava.util.Date; source: shared objects file (top)")
    -                           .shouldContain("restore: [Ljava.util.Date; with class loader: boot")
    -                           .shouldContain("[[Ljava.util.Date; source: shared objects file (top)")
    -                           .shouldContain("restore: [[Ljava.util.Date; with class loader: boot")
    -                           .shouldContain("[[[Ljava.util.Date; source: shared objects file (top)")
    -                           .shouldContain("restore: [[[Ljava.util.Date; with class loader: boot")
    -                           .shouldHaveExitValue(0);
    -                 });
    -
    -        // Case 3
    -        // Create a dynamic archive with primitive arrays [[J and [[[J with [J
    -        // already in the default CDS archive
    -        topArchiveName = getNewArchiveName();
    -        dump2(null, topArchiveName,
    -              "-Xlog:class+load,cds+dynamic=debug,cds+class=debug",
    -              "-cp", appJar, mainClass, "primitive")
    -              .assertNormalExit(output -> {
    -                     output.shouldMatch("cds.class.*klasses.*array \\[\\[J")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[\\[J");
    -                 });
    -
    -        // Case 3
    -        // At runtime, the [J should be loaded from the default CDS archive;
    -        // the higher-dimension array should be loaded from the dynamic archive.
    -        run2(null, topArchiveName, runtimeLogOptions,
    -             "-cp", appJar, mainClass, "primitive")
    -             .assertNormalExit(output -> {
    -                     output.shouldContain("[J source: shared objects file")
    -                           .shouldContain("restore: [J with class loader: boot")
    -                           .shouldContain("[[J source: shared objects file (top)")
    -                           .shouldContain("restore: [[J with class loader: boot")
    -                           .shouldContain("[[[J source: shared objects file (top)")
    -                           .shouldContain("restore: [[[J with class loader: boot")
    -                           .shouldHaveExitValue(0);
    -                 });
    -
    -        // Case 4
    -        // Create a dynamic archive with 2-, 3- and 4-dimension arrays of java/lang/Integer.
    -        // The java/lang/Integer class and the 1-dimension array is in the default archive.
    -        topArchiveName = getNewArchiveName();
    -        dump2(null, topArchiveName,
    -              "-Xlog:class+load,cds+dynamic=debug,cds+class=debug",
    -              "-cp", appJar, mainClass, "integer-array")
    -              .assertNormalExit(output -> {
    -                     output.shouldMatch("cds.class.*klasses.*array \\[\\[Ljava.lang.Integer;")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[\\[Ljava.lang.Integer;")
    -                           .shouldMatch("cds.class.*klasses.*array \\[\\[\\[\\[Ljava.lang.Integer;");
    -                 });
    -
    -        // Case 4
    -        // At runtime, the 4-dimension array of java/lang/Integer should be
    -        // loaded from the dynamic archive.
    -        run2(null, topArchiveName, runtimeLogOptions,
    -             "-cp", appJar, mainClass, "integer-array")
    -             .assertNormalExit(output -> {
    -                     output.shouldContain("java.lang.Integer source: shared objects file")
    -                           .shouldContain("restore: java.lang.Integer with class loader: boot")
    -                           .shouldContain("restore: [Ljava.lang.Integer; with class loader: boot")
    -                           .shouldContain("[[Ljava.lang.Integer; source: shared objects file (top)")
    -                           .shouldContain("restore: [[Ljava.lang.Integer; with class loader: boot")
    -                           .shouldContain("[[[Ljava.lang.Integer; source: shared objects file (top)")
    -                           .shouldContain("restore: [[[Ljava.lang.Integer; with class loader: boot")
    -                           .shouldContain("[[[[Ljava.lang.Integer; source: shared objects file (top)")
    -                           .shouldContain("restore: [[[[Ljava.lang.Integer; with class loader: boot")
    -                           .shouldHaveExitValue(0);
    -                 });
    -    }
    -}
    diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java
    deleted file mode 100644
    index 3c78e3e8dbe..00000000000
    --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java
    +++ /dev/null
    @@ -1,77 +0,0 @@
    -/*
    - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
    - * 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.lang.reflect.Array;
    -import java.util.Date;
    -
    -public class ArrayKlassesApp {
    -    public static void main(String args[]) {
    -        if (args.length == 1) {
    -            if (args[0].equals("system")) {
    -                Date[][][] array = new Date[1][2][2];
    -                int count = 0;
    -                for (int i=0; i<1; i++) {
    -                    for (int j=0; j<2; j++) {
    -                        for (int k=0; k<2; k++) {
    -                            array[i][j][k] = new Date();
    -                            count++;
    -                            array[i][j][k].setTime(20000 * count);
    -                        }
    -                    }
    -                }
    -            } else if (args[0].equals("primitive")) {
    -                long[][][] larray = new long[1][2][2];
    -                long lcount = 0;
    -                for (int i=0; i<1; i++) {
    -                    for (int j=0; j<2; j++) {
    -                        for (int k=0; k<2; k++) {
    -                            lcount++;
    -                            larray[i][j][k] = lcount;
    -                        }
    -                    }
    -                }
    -            } else if (args[0].equals("integer-array")) {
    -                Integer[][][][] iarray = new Integer[4][4][4][4];
    -                int count = 0;
    -                for (int i = 0; i < 4; i++) {
    -                    for (int j = 0; j < 4; j++) {
    -                        for (int k = 0; k < 4; k++) {
    -                            for (int l = 0; l < 4; l++) {
    -                                count++;
    -                                iarray[i][j][k][l] = new Integer(count);
    -                            }
    -                        }
    -                    }
    -                }
    -                System.out.println(iarray);
    -                System.out.println(iarray.getClass());
    -            }
    -        } else {
    -            Object x = Array.newInstance(ArrayKlassesApp.class, 3,3,3);
    -            System.out.println(x);
    -            System.out.println(x.getClass());
    -            System.out.println(Array.getLength(x));
    -        }
    -    }
    -}
    diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java
    index e0491b4b89f..0684ad1aacc 100644
    --- a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java
    +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2018 by SAP AG, Walldorf, Germany.
    + * Copyright (c) 2018, 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
    diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java
    index 373ee584679..d13e5e5d3c1 100644
    --- a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java
    +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2018 by SAP AG, Walldorf, Germany.
    + * Copyright (c) 2018, 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
    diff --git a/test/hotspot/jtreg/runtime/execstack/Test.java b/test/hotspot/jtreg/runtime/execstack/Test.java
    index 67891a523aa..22063a56dd5 100644
    --- a/test/hotspot/jtreg/runtime/execstack/Test.java
    +++ b/test/hotspot/jtreg/runtime/execstack/Test.java
    @@ -1,6 +1,6 @@
     /*
      * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
    - * Copyright (c) 2011 SAP AG.  All Rights Reserved.
    + * Copyright (c) 2011, 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
    diff --git a/test/hotspot/jtreg/runtime/execstack/TestMT.java b/test/hotspot/jtreg/runtime/execstack/TestMT.java
    index 0be1a461c0a..282b09f9a35 100644
    --- a/test/hotspot/jtreg/runtime/execstack/TestMT.java
    +++ b/test/hotspot/jtreg/runtime/execstack/TestMT.java
    @@ -1,6 +1,6 @@
     /*
      * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
    - * Copyright (c) 2011 SAP AG.  All Rights Reserved.
    + * Copyright (c) 2011, 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
    diff --git a/test/hotspot/jtreg/runtime/execstack/libtest-rw.c b/test/hotspot/jtreg/runtime/execstack/libtest-rw.c
    index 7ad4b95d25e..52e1e8328a1 100644
    --- a/test/hotspot/jtreg/runtime/execstack/libtest-rw.c
    +++ b/test/hotspot/jtreg/runtime/execstack/libtest-rw.c
    @@ -1,6 +1,6 @@
     /*
      * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
    - * Copyright (c) 2011 SAP AG.  All Rights Reserved.
    + * Copyright (c) 2011, 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
    diff --git a/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c b/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c
    index bce4f853106..e0d8e424809 100644
    --- a/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c
    +++ b/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c
    @@ -1,6 +1,6 @@
     /*
      * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
    - * Copyright (c) 2011 SAP AG.  All Rights Reserved.
    + * Copyright (c) 2011, 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
    diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java
    index 6c465b357d7..10888dce1b4 100644
    --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java
    +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java
    @@ -23,23 +23,30 @@
     
     /*
      * @test
    - *
      * @bug 8316694
      * @summary Verify that nmethod relocation posts the correct JVMTI events
    - * @requires vm.jvmti
    - * @requires vm.gc == "null" | vm.gc == "Serial"
    + * @requires vm.jvmti &
    + *           vm.gc != "Epsilon" &
    + *           vm.flavor == "server" &
    + *           !vm.emulatedClient &
    + *           (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
      * @library /test/lib /test/hotspot/jtreg
      * @build jdk.test.whitebox.WhiteBox
      * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    - * @run main/othervm/native NMethodRelocationTest
    + * @run main/othervm/native -agentlib:NMethodRelocationTest
    + *                          --enable-native-access=ALL-UNNAMED
    + *                          -Xbootclasspath/a:.
    + *                          -Xbatch
    + *                          -XX:+UnlockDiagnosticVMOptions
    + *                          -XX:+WhiteBoxAPI
    + *                          -XX:+SegmentedCodeCache
    + *                          -XX:-TieredCompilation
    + *                          -XX:+UnlockExperimentalVMOptions
    + *                          -XX:+NMethodRelocation
    + *                          NMethodRelocationTest
      */
     
    -import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION;
    -
     import java.lang.reflect.Executable;
    -import java.util.Objects;
    -import java.util.regex.Matcher;
    -import java.util.regex.Pattern;
     
     import jdk.test.lib.Asserts;
     import jdk.test.lib.process.OutputAnalyzer;
    @@ -48,52 +55,9 @@ import jdk.test.whitebox.WhiteBox;
     import jdk.test.whitebox.code.BlobType;
     import jdk.test.whitebox.code.NMethod;
     
    +import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION;
     
     public class NMethodRelocationTest {
    -    public static void main(String[] args) throws Exception {
    -        ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
    -                "-agentlib:NMethodRelocationTest",
    -                "--enable-native-access=ALL-UNNAMED",
    -                "-Xbootclasspath/a:.",
    -                "-XX:+UseSerialGC",
    -                "-XX:+UnlockDiagnosticVMOptions",
    -                "-XX:+WhiteBoxAPI",
    -                "-XX:+SegmentedCodeCache",
    -                "-XX:-TieredCompilation",
    -                "-XX:+UnlockExperimentalVMOptions",
    -                "-XX:+NMethodRelocation",
    -                "DoWork");
    -
    -        OutputAnalyzer oa = new OutputAnalyzer(pb.start());
    -        String output = oa.getOutput();
    -        if (oa.getExitValue() != 0) {
    -            System.err.println(oa.getOutput());
    -            throw new RuntimeException("Non-zero exit code returned from the test");
    -        }
    -        Asserts.assertTrue(oa.getExitValue() == 0);
    -
    -        Pattern pattern = Pattern.compile("(?m)^Relocated nmethod from (0x[0-9a-f]{16}) to (0x[0-9a-f]{16})$");
    -        Matcher matcher = pattern.matcher(output);
    -
    -        if (matcher.find()) {
    -            String fromAddr = matcher.group(1);
    -            String toAddr = matcher.group(2);
    -
    -            // Confirm events sent for both original and relocated nmethod
    -            oa.shouldContain(":   name: compiledMethod, code: " + fromAddr);
    -            oa.shouldContain(":   name: compiledMethod, code: " + toAddr);
    -            oa.shouldContain(":   name: compiledMethod, code: " + fromAddr);
    -            oa.shouldContain(":   name: compiledMethod, code: " + toAddr);
    -        } else {
    -            System.err.println(oa.getOutput());
    -            throw new RuntimeException("Unable to find relocation information");
    -        }
    -    }
    -}
    -
    -class DoWork {
    -
    -    protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
     
         /** Load native library if required. */
         static {
    @@ -107,43 +71,18 @@ class DoWork {
             }
         }
     
    -    /**
    -     * Returns value of VM option.
    -     *
    -     * @param name option's name
    -     * @return value of option or {@code null}, if option doesn't exist
    -     * @throws NullPointerException if name is null
    -     */
    -    protected static String getVMOption(String name) {
    -        Objects.requireNonNull(name);
    -        return Objects.toString(WHITE_BOX.getVMFlag(name), null);
    -    }
    +    protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
     
    -    /**
    -     * Returns value of VM option or default value.
    -     *
    -     * @param name         option's name
    -     * @param defaultValue default value
    -     * @return value of option or {@code defaultValue}, if option doesn't exist
    -     * @throws NullPointerException if name is null
    -     * @see #getVMOption(String)
    -     */
    -    protected static String getVMOption(String name, String defaultValue) {
    -        String result = getVMOption(name);
    -        return result == null ? defaultValue : result;
    -    }
    +    native static boolean shouldExit();
     
    -    public static void main(String argv[]) throws Exception {
    -        run();
    -    }
    -
    -    public static void run() throws Exception {
    -        Executable method = DoWork.class.getDeclaredMethod("compiledMethod");
    +    public static void main(String[] argv) throws Exception {
    +        Executable method = NMethodRelocationTest.class.getDeclaredMethod("compiledMethod");
             WHITE_BOX.testSetDontInlineMethod(method, true);
     
             WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION);
    -        while (WHITE_BOX.isMethodQueuedForCompilation(method)) {
    -            Thread.onSpinWait();
    +
    +        if (!WHITE_BOX.isMethodCompiled(method)) {
    +            throw new AssertionError("Method not compiled");
             }
     
             NMethod originalNMethod = NMethod.get(method, false);
    @@ -164,13 +103,9 @@ class DoWork {
     
             WHITE_BOX.deoptimizeAll();
     
    -        WHITE_BOX.fullGC();
    -        WHITE_BOX.fullGC();
    -
    -        WHITE_BOX.lockCompilation();
    -
    -        System.out.printf("Relocated nmethod from 0x%016x to 0x%016x%n", originalNMethod.code_begin, relocatedNMethod.code_begin);
    -        System.out.flush();
    +        while (!shouldExit()) {
    +            WHITE_BOX.fullGC();
    +        }
         }
     
         public static long compiledMethod() {
    diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp
    index 41ba6b10608..7f8ab7b78bc 100644
    --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp
    +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp
    @@ -21,10 +21,20 @@
      * questions.
      */
     
    +#include 
     #include 
    -#include 
    -#include 
    -#include 
    +#include "jvmti_common.hpp"
    +
    +extern "C" {
    +
    +// Track nmethod addresses for LOAD and UNLOAD events
    +static const void* first_load_addr      = nullptr;
    +static const void* second_load_addr     = nullptr;
    +static const void* first_unload_addr    = nullptr;
    +static const void* second_unload_addr   = nullptr;
    +
    +// Keep track of test completion
    +static std::atomic should_exit{false};
     
     /**
      * Callback for COMPILED_METHOD_LOAD event.
    @@ -34,18 +44,27 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
                                 jint code_size, const void* code_addr,
                                 jint map_length, const jvmtiAddrLocationMap* map,
                                 const void* compile_info) {
    -    char* name = nullptr;
    -    char* sig = nullptr;
     
    -    if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) {
    -        printf("    [Could not retrieve method name]\n");
    -        fflush(stdout);
    +    // Only track events for "compiledMethod"
    +    char* name = get_method_name(jvmti, method);
    +    if (strcmp(name, "compiledMethod") != 0) {
             return;
         }
     
    -    printf(":   name: %s, code: 0x%016" PRIxPTR "\n",
    -        name, (uintptr_t)code_addr);
    -    fflush(stdout);
    +    LOG(":   name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr);
    +
    +    if (first_load_addr == nullptr) {
    +        first_load_addr = code_addr;
    +    } else if (second_load_addr == nullptr) {
    +        second_load_addr = code_addr;
    +
    +        // Verify that the addresses are different
    +        if (first_load_addr == second_load_addr) {
    +            fatal("Load events for 'compiledMethod' are expected to use different addresses");
    +        }
    +    } else {
    +        fatal("Received too many load events for 'compiledMethod'");
    +    }
     }
     
     /**
    @@ -54,25 +73,50 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
     JNIEXPORT void JNICALL
     callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method,
                                  const void* code_addr) {
    -    char* name = nullptr;
    -    char* sig = nullptr;
     
    -    if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) {
    -        printf("    [Could not retrieve method name]\n");
    -        fflush(stdout);
    +    // Only track events for "compiledMethod"
    +    char* name = get_method_name(jvmti, method);
    +    if (strcmp(name, "compiledMethod") != 0) {
             return;
         }
    -    printf(":   name: %s, code: 0x%016" PRIxPTR "\n",
    -        name, (uintptr_t)code_addr);
    -    fflush(stdout);
    +
    +    LOG(":   name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr);
    +
    +    // Validate both loads have occurred
    +    if (first_load_addr == nullptr || second_load_addr == nullptr) {
    +        fatal("UNLOAD event for 'compiledMethod' occurred before both LOAD events");
    +    }
    +
    +    if (first_unload_addr == nullptr) {
    +        first_unload_addr = code_addr;
    +    } else if (second_unload_addr == nullptr) {
    +        second_unload_addr = code_addr;
    +
    +        // Verify that the addresses are different
    +        if (first_unload_addr == second_unload_addr) {
    +            fatal("Unload events for 'compiledMethod' are expected to use different addresses");
    +        }
    +
    +        // LOAD and UNLOAD events should report the same two addresses, but the order of
    +        // the UNLOADs is not guaranteed, since the GC may unload either nmethod first.
    +        if ((first_load_addr == first_unload_addr  && second_load_addr == second_unload_addr) ||
    +            (first_load_addr == second_unload_addr && second_load_addr == first_unload_addr)) {
    +
    +            // Update should_exit to signal test completion
    +            should_exit.store(true);
    +        } else {
    +            fatal("Address mismatch for 'compiledMethod' events");
    +        }
    +    } else {
    +        fatal("Received too many unload events for 'compiledMethod'");
    +    }
     }
     
     JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
         jvmtiEnv* jvmti = nullptr;
    -    jvmtiError error;
     
         if (jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0) != JNI_OK) {
    -        printf("Unable to access JVMTI!\n");
    +        LOG("Unable to access JVMTI!\n");
             return JNI_ERR;
         }
     
    @@ -80,11 +124,8 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
         jvmtiCapabilities caps;
         memset(&caps, 0, sizeof(caps));
         caps.can_generate_compiled_method_load_events = 1;
    -    error = jvmti->AddCapabilities(&caps);
    -    if (error != JVMTI_ERROR_NONE) {
    -        printf("ERROR: Unable to add capabilities, error=%d\n", error);
    -        return JNI_ERR;
    -    }
    +    jvmtiError error = jvmti->AddCapabilities(&caps);
    +    check_jvmti_error(error, "Unable to add capabilities");
     
         // Set event callbacks
         jvmtiEventCallbacks eventCallbacks;
    @@ -92,23 +133,21 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
         eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad;
         eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload;
         error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks));
    -    if (error != JVMTI_ERROR_NONE) {
    -        printf("ERROR: Unable to set event callbacks, error=%d\n", error);
    -        return JNI_ERR;
    -    }
    +    check_jvmti_error(error, "Unable to set event callbacks");
     
         // Enable events
         error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, nullptr);
    -    if (error != JVMTI_ERROR_NONE) {
    -        printf("ERROR: Unable to enable COMPILED_METHOD_LOAD event, error=%d\n", error);
    -        return JNI_ERR;
    -    }
    +    check_jvmti_error(error, "Unable to enable COMPILED_METHOD_LOAD event");
     
         error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, nullptr);
    -    if (error != JVMTI_ERROR_NONE) {
    -        printf("ERROR: Unable to enable COMPILED_METHOD_UNLOAD event, error=%d\n", error);
    -        return JNI_ERR;
    -    }
    +    check_jvmti_error(error, "Unable to enable COMPILED_METHOD_UNLOAD event");
     
         return JNI_OK;
     }
    +
    +JNIEXPORT jboolean JNICALL
    +Java_NMethodRelocationTest_shouldExit(JNIEnv *env, jclass cls) {
    +  return should_exit.load();
    +}
    +
    +}
    diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
    index 079f65620d8..2a5c9bea111 100644
    --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
    +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.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
    @@ -45,8 +45,9 @@ abstract class TestTask implements Runnable {
         }
     
         public void ensureReadyAndWaiting(Thread vt, Thread.State expState, ReentrantLock rlock) {
    +        sleep(50); // reliability: wait for a potential class loading to complete
             // wait while the thread is not ready or thread state is unexpected
    -        while (!threadReady || (vt.getState() != expState) || !rlock.hasQueuedThreads()) {
    +        while (!threadReady || (vt.getState() != expState) || !rlock.hasQueuedThread(vt)) {
                 sleep(1);
             }
         }
    @@ -125,11 +126,12 @@ public class ThreadListStackTracesTest {
             int jvmtiExpState = (expState == Thread.State.WAITING) ?
                                 JVMTI_THREAD_STATE_WAITING :
                                 JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
    +        Thread.State state = vt.getState();
     
    -        System.out.printf("State: expected: %s single: %x multi: %x\n",
    -                          vt.getState(), singleState, multiState);
    +        System.out.printf("State: expected: %s, vt.getState(): %s, jvmtiExpState: %x single: %x multi: %x\n",
    +                          expState, state, jvmtiExpState, singleState, multiState);
     
    -        if (vt.getState() != expState) {
    +        if (state != expState) {
                 failed("Java thread state is wrong");
             }
             if ((singleState & jvmtiExpState) == 0) {
    diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java
    index c97353006d4..d88e34ae5e0 100644
    --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java
    +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2007, 2020, 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
    @@ -28,6 +28,7 @@
      * @summary converted from VM Testbase gc/gctests/MemoryEaterMT.
      * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent]
      *
    + * @requires test.thread.factory != "Virtual"
      * @library /vmTestbase
      *          /test/lib
      * @run main/othervm -XX:-UseGCOverheadLimit gc.gctests.MemoryEaterMT.MemoryEaterMT
    diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java
    index 7c089abb6ec..e2f1b9cbc71 100644
    --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java
    +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.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
    @@ -24,15 +24,19 @@
     
     /*
      * @test
    - * @key randomness
      *
      * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001.
      * VM Testbase keywords: [monitoring]
      *
    - * @requires vm.opt.DisableExplicitGC != "true"
      * @library /vmTestbase
      *          /test/lib
    - * @run main/othervm -XX:-UseGCOverheadLimit
    + *
    + * @build jdk.test.whitebox.WhiteBox
    + * @requires vm.opt.DisableExplicitGC != "true"
    + * @requires vm.compMode != "Xcomp"
    + *
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit
      *      nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001
      *      -testMode=directly
      *      -iterations=5
    @@ -42,10 +46,12 @@ package nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCount
     
     import java.util.List;
     import java.lang.management.*;
    +
    +import jdk.test.whitebox.WhiteBox;
    +
     import nsk.share.TestFailure;
     import nsk.share.test.*;
     import nsk.monitoring.share.*;
    -import nsk.share.gc.Algorithms;
     import nsk.share.runner.RunParams;
     import nsk.share.runner.RunParamsAware;
     
    @@ -80,12 +86,15 @@ public class CollectionCounters001 extends MonitoringTestBase implements RunPara
             private void runOne(ExecutionController stresser) {
                     updateCounters();
                     validate(false /* don't check gc count increases */);
    -                Algorithms.eatMemory(stresser);
    +
    +                WhiteBox.getWhiteBox().fullGC();
                     updateCounters();
                     validate(true);
    +
                     System.gc();
                     updateCounters();
                     validate(true);
    +
                     memory.gc();
                     updateCounters();
                     validate(true);
    diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.java
    index 51abb4e7a0b..2c1e21fcb15 100644
    --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.java
    +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.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
    @@ -24,15 +24,19 @@
     
     /*
      * @test
    - * @key randomness
      *
      * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002.
      * VM Testbase keywords: [monitoring]
      *
    - * @requires vm.opt.DisableExplicitGC != "true"
      * @library /vmTestbase
      *          /test/lib
    - * @run main/othervm -XX:-UseGCOverheadLimit
    + *
    + * @build jdk.test.whitebox.WhiteBox
    + * @requires vm.opt.DisableExplicitGC != "true"
    + * @requires vm.compMode != "Xcomp"
    + *
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit
      *      nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001
      *      -testMode=server
      *      -MBeanServer=default
    diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.java
    index 20e266f50f1..241b1912d0f 100644
    --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.java
    +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.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
    @@ -24,15 +24,19 @@
     
     /*
      * @test
    - * @key randomness
      *
      * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003.
      * VM Testbase keywords: [monitoring]
      *
    - * @requires vm.opt.DisableExplicitGC != "true"
      * @library /vmTestbase
      *          /test/lib
    - * @run main/othervm -XX:-UseGCOverheadLimit
    + *
    + * @build jdk.test.whitebox.WhiteBox
    + * @requires vm.opt.DisableExplicitGC != "true"
    + * @requires vm.compMode != "Xcomp"
    + *
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit
      *      nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001
      *      -testMode=server
      *      -MBeanServer=custom
    diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java
    index db5a04cede8..cd0ed660ed0 100644
    --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java
    +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.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
    @@ -24,15 +24,19 @@
     
     /*
      * @test
    - * @key randomness
      *
      * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004.
      * VM Testbase keywords: [monitoring]
      *
    - * @requires vm.opt.DisableExplicitGC != "true"
      * @library /vmTestbase
      *          /test/lib
    - * @run main/othervm -XX:-UseGCOverheadLimit
    + *
    + * @build jdk.test.whitebox.WhiteBox
    + * @requires vm.opt.DisableExplicitGC != "true"
    + * @requires vm.compMode != "Xcomp"
    + *
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit
      *      nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001
      *      -testMode=proxy
      *      -MBeanServer=default
    diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.java
    index 68c50f026b7..c3c6fac62e7 100644
    --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.java
    +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.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
    @@ -24,15 +24,19 @@
     
     /*
      * @test
    - * @key randomness
      *
      * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005.
      * VM Testbase keywords: [monitoring]
      *
    - * @requires vm.opt.DisableExplicitGC != "true"
      * @library /vmTestbase
      *          /test/lib
    - * @run main/othervm -XX:-UseGCOverheadLimit
    + *
    + * @build jdk.test.whitebox.WhiteBox
    + * @requires vm.opt.DisableExplicitGC != "true"
    + * @requires vm.compMode != "Xcomp"
    + *
    + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
    + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit
      *      nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001
      *      -testMode=proxy
      *      -MBeanServer=custom
    diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java
    index 404dc6498f7..518a932e16b 100644
    --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java
    +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -73,10 +73,7 @@ public class Algorithms {
              * 

    * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * This method uses nsk.share.test.Stresser class to control * it's execution. Consumed number of iterations depends on diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java index 1c7a8c6172c..529b299fd15 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.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 @@ -181,10 +181,7 @@ public final class GarbageUtils { * Eat memory using default(byte[]) garbage producer. * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * @param stresser stresser * @param initialFactor determines which portion of initial memory initial chunk will be @@ -200,10 +197,7 @@ public final class GarbageUtils { * Eat memory using given garbage producer. * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * @param stresser stresser to use * @param gp garbage producer @@ -270,10 +264,7 @@ public final class GarbageUtils { * Eat memory using given garbage producer. * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * @param stresser stresser to use * @param gp garbage producer diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index addcc8830af..291b2163b0d 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -561,8 +561,6 @@ java/net/MulticastSocket/Test.java 7145658,8308807 # jdk_nio -java/nio/channels/AsyncCloseAndInterrupt.java 8368290 macosx-26.0.1 - java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8308807,8144003 aix-ppc64,macosx-all java/nio/channels/DatagramChannel/AfterDisconnect.java 8308807 aix-ppc64 java/nio/channels/DatagramChannel/ManySourcesAndTargets.java 8264385 macosx-aarch64 @@ -712,7 +710,6 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- # jdk_jfr jdk/jfr/event/compiler/TestCodeSweeper.java 8338127 generic-all -jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java 8371014 aix-ppc64,linux-ppc64le jdk/jfr/event/oldobject/TestShenandoah.java 8342951 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 jdk/jfr/jvm/TestWaste.java 8371630 generic-all diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java new file mode 100644 index 00000000000..eb903c0a224 --- /dev/null +++ b/test/jdk/build/CheckFiles.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2026 SAP SE. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import jdk.test.lib.Platform; + +/* + * @test + * @summary Check for unwanted file (types/extensions) in the jdk image + * @library /test/lib + * @requires !vm.debug + * @run main CheckFiles + */ +public class CheckFiles { + + // Set this property on command line to scan an alternate dir or file: + // JTREG=JAVA_OPTIONS=-Djdk.test.build.CheckFiles.dir=/path/to/dir + public static final String DIR_PROPERTY = "jdk.test.build.CheckFiles.dir"; + + public static void main(String[] args) throws Exception { + String jdkPathString = System.getProperty("test.jdk"); + Path jdkHome = Paths.get(jdkPathString); + + Path mainDirToScan = jdkHome; + String overrideDir = System.getProperty(DIR_PROPERTY); + if (overrideDir != null) { + mainDirToScan = Paths.get(overrideDir); + } + + System.out.println("Main directory to scan:" + mainDirToScan); + Path binDir = mainDirToScan.resolve("bin"); + Path libDir = mainDirToScan.resolve("lib"); + Path includeDir = mainDirToScan.resolve("include"); + Path jmodsDir = mainDirToScan.resolve("jmods"); + + System.out.println("Bin directory to scan:" + binDir); + ArrayList allowedEndingsBinDir = new ArrayList<>(); + // UNIX - no extensions are allowed; Windows : .dll, .exe, .pdb, .jsa + if (Platform.isWindows()) { + allowedEndingsBinDir.add(".dll"); + allowedEndingsBinDir.add(".exe"); + allowedEndingsBinDir.add(".pdb"); + allowedEndingsBinDir.add(".jsa"); + } + boolean binDirRes = scanFiles(binDir, allowedEndingsBinDir); + + System.out.println("Lib directory to scan:" + libDir); + ArrayList allowedEndingsLibDir = new ArrayList<>(); + allowedEndingsLibDir.add(".jfc"); // jfr config files + allowedEndingsLibDir.add("cacerts"); + allowedEndingsLibDir.add("blocked.certs"); + allowedEndingsLibDir.add("public_suffix_list.dat"); + allowedEndingsLibDir.add("classlist"); + allowedEndingsLibDir.add("fontconfig.bfc"); + allowedEndingsLibDir.add("fontconfig.properties.src"); + allowedEndingsLibDir.add("ct.sym"); + allowedEndingsLibDir.add("jrt-fs.jar"); + allowedEndingsLibDir.add("jvm.cfg"); + allowedEndingsLibDir.add("modules"); + allowedEndingsLibDir.add("psfontj2d.properties"); + allowedEndingsLibDir.add("psfont.properties.ja"); + allowedEndingsLibDir.add("src.zip"); + allowedEndingsLibDir.add("tzdb.dat"); + if (Platform.isWindows()) { + allowedEndingsLibDir.add(".lib"); + allowedEndingsLibDir.add("tzmappings"); + } else { + allowedEndingsLibDir.add("jexec"); + allowedEndingsLibDir.add("jspawnhelper"); + allowedEndingsLibDir.add(".jsa"); + if (Platform.isOSX()) { + allowedEndingsLibDir.add("shaders.metallib"); + allowedEndingsLibDir.add(".dylib"); + } else { + allowedEndingsLibDir.add(".so"); + } + if (Platform.isAix()) { + allowedEndingsLibDir.add("tzmappings"); + } + } + boolean libDirRes = scanFiles(libDir, allowedEndingsLibDir); + + if (binDirRes) { + System.out.println("Bin directory scan successful."); + } else { + throw new Error("bin dir scan failed"); + } + + if (libDirRes) { + System.out.println("Lib directory scan successful."); + } else { + throw new Error("lib dir scan failed"); + } + + if (Files.isDirectory(includeDir)) { + System.out.println("Include directory to scan:" + includeDir); + ArrayList allowedEndingsIncludeDir = new ArrayList<>(); + allowedEndingsIncludeDir.add(".h"); + allowedEndingsIncludeDir.add(".hpp"); + boolean includeDirRes = scanFiles(includeDir, allowedEndingsIncludeDir); + if (includeDirRes) { + System.out.println("Include directory scan successful."); + } else { + throw new Error("include dir scan failed"); + } + } + + // when enabling "JEP 493: Linking Run-Time Images without JMODs" we do not + // have the jmods folder at all, so first test the presence of the folder + if (Files.isDirectory(jmodsDir)) { + System.out.println("Jmods directory to scan:" + jmodsDir); + ArrayList allowedEndingsJmodsDir = new ArrayList<>(); + allowedEndingsJmodsDir.add(".jmod"); + boolean jmodsDirRes = scanFiles(jmodsDir, allowedEndingsJmodsDir); + if (jmodsDirRes) { + System.out.println("Jmods directory scan successful."); + } else { + throw new Error("jmods dir scan failed"); + } + } + } + + private static boolean scanFiles(Path root, ArrayList allowedEndings) throws IOException { + AtomicBoolean badFileFound = new AtomicBoolean(false); + + Files.walkFileTree(root, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String fullFileName = file.toString(); + String fileName = file.getFileName().toString(); + System.out.println(" visiting file:" + fullFileName); + checkFile(fileName, allowedEndings); + return super.visitFile(file, attrs); + } + + private void checkFile(String name, ArrayList allowedEndings) { + if (allowedEndings.isEmpty()) { // no file extensions allowed + int lastDot = name.lastIndexOf('.'); + if (lastDot > 0) { + System.out.println(" --> ERROR this file is not allowed:" + name); + badFileFound.set(true); + } + } else { + boolean allowed = allowedEndings.stream().anyMatch(name::endsWith); + if (! allowed) { + System.out.println(" --> ERROR this file is not allowed:" + name); + badFileFound.set(true); + } + } + } + }); + + return !badFileFound.get(); + } +} diff --git a/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java b/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java index 19bc6d95c39..9694043d1bb 100644 --- a/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java +++ b/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.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 @@ -29,10 +29,14 @@ * @run main XBMDecoderTest */ +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.PrintStream; +import java.util.Arrays; import javax.swing.ImageIcon; public class XBMDecoderTest { @@ -57,21 +61,39 @@ public class XBMDecoderTest { ImageIcon icon = new ImageIcon(fis.readAllBytes()); boolean isErrEmpty = errContent.toString().isEmpty(); + if (!isErrEmpty) { System.out.println("Expected ImageFormatException occurred."); System.out.print(errContent); } - if (validCase && !isErrEmpty) { throw new RuntimeException("Test failed: Error stream not empty"); - } else if (!validCase && isErrEmpty) { + } else if (!validCase && isErrEmpty && hasPixelData(icon.getImage())) { throw new RuntimeException("Test failed: ImageFormatException" + " expected but not thrown"); } + if (validCase && !hasPixelData(icon.getImage())) { + throw new RuntimeException("Test failed: the parsed image " + + "does not contain any pixel data"); + } System.out.println("PASSED\n"); } finally { System.setErr(originalErr); } } } + + private static boolean hasPixelData(Image img) { + int w = img.getWidth(null); + int h = img.getHeight(null); + BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = bi.createGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + int[] pixels = bi.getRGB(0, 0, w, h, null, 0, w); + if (Arrays.stream(pixels).allMatch(i -> i == 0)) { + return false; + } + return true; + } } diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm new file mode 100644 index 00000000000..5cfb8e21cf8 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm @@ -0,0 +1,6 @@ +#define test_width 16 +#define test_height 3 +#define ht_x 1 +#define ht_y 2 +static unsigned char test_bits[] = { +}; diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm index c6f819582d0..1286eee1d9b 100644 --- a/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm @@ -1,3 +1,3 @@ -#define k_wt 16 -#define k_ht 1 +#define k_width 16 +#define k_height 1 k[] = { 0x10, 1234567890}; diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm new file mode 100644 index 00000000000..714907084f2 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm @@ -0,0 +1,3 @@ +#define test_width 16 +#define test_height 2 +static unsigned char test_bits[] = { 0x13, 0x11, 0xAB+, 0xff }; \ No newline at end of file diff --git a/test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm b/test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm new file mode 100644 index 00000000000..e24bc10e9b0 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm @@ -0,0 +1,8 @@ +#define test_width 16 +#define test_height 3 +#define ht_x 1 +#define ht_y 2 +static unsigned char test_bits[] = { +0x20, 0x10, +0x25, 0x01, +0xAC, 0xab }; diff --git a/test/jdk/java/foreign/TestStringEncoding.java b/test/jdk/java/foreign/TestStringEncoding.java index 94732943b9d..e9e47420a68 100644 --- a/test/jdk/java/foreign/TestStringEncoding.java +++ b/test/jdk/java/foreign/TestStringEncoding.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; +import java.util.Set; import java.util.function.UnaryOperator; import jdk.internal.foreign.AbstractMemorySegmentImpl; @@ -102,6 +103,140 @@ public class TestStringEncoding { } } + @Test(dataProvider = "strings") + public void testStringsLength(String testString) { + if (!testString.isEmpty()) { + for (Charset charset : Charset.availableCharsets().values()) { + if (charset.canEncode()) { + for (Arena arena : arenas()) { + try (arena) { + MemorySegment text = arena.allocateFrom(testString, charset, 0, testString.length()); + long length = text.byteSize(); + assertEquals(length, testString.getBytes(charset).length); + String roundTrip = text.getString(0, charset, length); + if (charset.newEncoder().canEncode(testString)) { + assertEquals(roundTrip, testString); + } + } + } + } + } + } + } + + @Test(dataProvider = "strings") + public void testStringsCopy(String testString) { + if (!testString.isEmpty()) { + for (Charset charset : Charset.availableCharsets().values()) { + if (charset.canEncode()) { + for (Arena arena : arenas()) { + try (arena) { + byte[] bytes = testString.getBytes(charset); + MemorySegment text = arena.allocate(JAVA_BYTE, bytes.length); + MemorySegment.copy(testString, charset, 0, text, 0, testString.length()); + String roundTrip = text.getString(0, charset, bytes.length); + if (charset.newEncoder().canEncode(testString)) { + assertEquals(roundTrip, testString); + } + } + } + } + } + } + } + + @Test + public void testStringsLengthNegative() { + try (Arena arena = Arena.ofConfined()) { + var segment = arena.allocateFrom("abc"); + assertThrows(IllegalArgumentException.class, () -> segment.getString(1, StandardCharsets.UTF_8, -1)); + } + } + + @Test + public void testCopyThrows() { + try (Arena arena = Arena.ofConfined()) { + String testString = "abc"; + String testString_notBytesCompatible = "snowman \u26C4"; + MemorySegment text = arena.allocate(JAVA_BYTE, 3); + MemorySegment text_notBytesCompatible = arena.allocate(JAVA_BYTE, + testString_notBytesCompatible.getBytes(StandardCharsets.UTF_8).length); + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, testString.length()); + MemorySegment.copy(testString_notBytesCompatible, StandardCharsets.UTF_8, 0, + text_notBytesCompatible, 0, + testString_notBytesCompatible.length()); + // srcIndex < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, -1, text, 0, testString.length())); + // dstOffset < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, -1, testString.length())); + // numChars < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, -1)); + // srcIndex + numChars > length + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 1, text, 0, testString.length())); + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, testString.length() + 1)); + // dstOffset > byteSize() - B + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 1, testString.length())); + // srcIndex + numChars overflows + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, Integer.MAX_VALUE, text, 0, Integer.MAX_VALUE + 3)); + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString_notBytesCompatible, StandardCharsets.UTF_8, Integer.MAX_VALUE, text, 0, Integer.MAX_VALUE + 3)); + } + } + + @Test + public void testAllocateFromThrows() { + try (Arena arena = Arena.ofConfined()) { + String testString = "abc"; + String testString_notBytesCompatible = "snowman \u26C4"; + arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length()); + arena.allocateFrom(testString, StandardCharsets.UTF_8, 2, 1); + // srcIndex < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, -1, testString.length())); + // numChars < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, -1)); + // srcIndex + numChars > length + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length() + 1)); + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 1, testString.length())); + // srcIndex + numChars overflows + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 3, Integer.MAX_VALUE)); + assertThrows(IndexOutOfBoundsException.class, () -> arena.allocateFrom( + testString_notBytesCompatible, StandardCharsets.UTF_8, 3, Integer.MAX_VALUE)); + } + } + + @Test + public void testGetStringThrows() { + try (Arena arena = Arena.ofConfined()) { + String testString = "abc"; + MemorySegment text = arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length()); + text.getString(0, StandardCharsets.UTF_8, 3); + // unsupported string size + assertThrows(IllegalArgumentException.class, () -> + text.getString(0, StandardCharsets.UTF_8, Integer.MAX_VALUE + 1L)); + // offset < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + text.getString(-1, StandardCharsets.UTF_8, 3)); + // offset > byteSize() - length + assertThrows(IndexOutOfBoundsException.class, () -> + text.getString(1, StandardCharsets.UTF_8, 3)); + // length < 0 + assertThrows(IllegalArgumentException.class, () -> + text.getString(0, StandardCharsets.UTF_8, -1)); + } + } + @Test(dataProvider = "strings") public void testStringsHeap(String testString) { for (Charset charset : singleByteCharsets()) { @@ -221,6 +356,74 @@ public class TestStringEncoding { } } + @Test(dataProvider = "strings") + public void testSubstringGetString(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : singleByteCharsets()) { + for (var arena: arenas()) { + try (arena) { + MemorySegment text = arena.allocateFrom(testString, charset, 0, testString.length()); + for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) { + for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) { + // this test assumes single-byte charsets + String roundTrip = text.getString(srcIndex, charset, numChars); + String substring = testString.substring(srcIndex, srcIndex + numChars); + assertEquals(roundTrip, substring); + } + } + } + } + } + } + + @Test(dataProvider = "strings") + public void testSubstringAllocate(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : singleByteCharsets()) { + for (var arena: arenas()) { + try (arena) { + for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) { + for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) { + MemorySegment text = arena.allocateFrom(testString, charset, srcIndex, numChars); + String substring = testString.substring(srcIndex, srcIndex + numChars); + assertEquals(text.byteSize(), substring.getBytes(charset).length); + String roundTrip = text.getString(0, charset, text.byteSize()); + assertEquals(roundTrip, substring); + } + } + } + } + } + } + + @Test(dataProvider = "strings") + public void testSubstringCopy(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : singleByteCharsets()) { + for (var arena: arenas()) { + try (arena) { + for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) { + for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) { + String substring = testString.substring(srcIndex, srcIndex + numChars); + long length = substring.getBytes(charset).length; + MemorySegment text = arena.allocate(JAVA_BYTE, length); + long copied = MemorySegment.copy(testString, charset, srcIndex, text, 0, numChars); + String roundTrip = text.getString(0, charset, length); + assertEquals(roundTrip, substring); + assertEquals(copied, length); + } + } + } + } + } + } + private static final MemoryLayout CHAR_POINTER = ADDRESS .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); private static final Linker LINKER = Linker.nativeLinker(); @@ -402,7 +605,7 @@ public class TestStringEncoding { {""}, {"X"}, {"12345"}, - {"yen \u00A5"}, + {"section \u00A7"}, {"snowman \u26C4"}, {"rainbow \uD83C\uDF08"}, {"0"}, diff --git a/test/jdk/java/io/File/GetXSpace.java b/test/jdk/java/io/File/GetXSpace.java index 96ce4ede1b2..f1529c18fb2 100644 --- a/test/jdk/java/io/File/GetXSpace.java +++ b/test/jdk/java/io/File/GetXSpace.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 @@ -44,7 +44,6 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import jdk.test.lib.Platform; -import jdk.test.lib.Platform; import static java.lang.System.err; import static java.lang.System.out; @@ -103,16 +102,11 @@ public class GetXSpace { private final long free; private final long available; - Space(String name) { + Space(String name) throws IOException { this.name = name; long[] sizes = new long[4]; if (Platform.isWindows() && isCDDrive(name)) { - try { - getCDDriveSpace(name, sizes); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException("can't get CDDrive sizes"); - } + getCDDriveSpace(name, sizes); } else { if (getSpace(name, sizes)) System.err.println("WARNING: total space is estimated"); @@ -170,7 +164,7 @@ public class GetXSpace { return al; } - private static void compare(Space s) { + private static void compare(Space s) throws IOException { File f = new File(s.name()); long ts = f.getTotalSpace(); long fs = f.getFreeSpace(); @@ -318,7 +312,7 @@ public class GetXSpace { } } - private static int testFile(Path dir) { + private static int testFile(Path dir) throws IOException { String dirName = dir.toString(); out.format("--- Testing %s%n", dirName); compare(new Space(dir.getRoot().toString())); @@ -333,10 +327,11 @@ public class GetXSpace { return fail != 0 ? 1 : 0; } - private static int testVolumes() { + private static int testVolumes() throws IOException { out.println("--- Testing volumes"); // Find all of the partitions on the machine and verify that the sizes - // returned by File::getXSpace are equivalent to those from getSpace or getCDDriveSpace + // returned by File::getXSpace are equivalent to those from getSpace + // or getCDDriveSpace ArrayList l; try { l = paths(); @@ -350,7 +345,18 @@ public class GetXSpace { throw new RuntimeException("no partitions?"); for (var p : l) { - Space s = new Space(p); + Space s; + try { + s = new Space(p); + } catch (IOException x) { + // Avoid failing for transient file systems on Windows + if (Platform.isWindows()) { + File f = new File(p); + if (!f.exists()) + continue; + } + throw new IOException("Failure for volume " + p, x); + } compare(s); compareZeroNonExist(); compareZeroExist(); @@ -408,19 +414,19 @@ public class GetXSpace { // size[2] free space: number of free bytes in the volume // size[3] usable space: number of bytes available to the caller // - private static native boolean getSpace0(String root, long[] space); + private static native boolean getSpace0(String root, long[] space) + throws IOException; private static native boolean isCDDrive(String root); - private static boolean getSpace(String root, long[] space) { + private static boolean getSpace(String root, long[] space) + throws IOException { try { return getSpace0(root, space); - } catch (RuntimeException e) { + } catch (IOException e) { File f = new File(root); - boolean exists = f.exists(); - boolean readable = f.canRead(); System.err.printf("getSpace0 failed for %s (%s, %s)%n", - root, exists, readable); + root, f.exists(), f.canRead()); throw e; } } diff --git a/test/jdk/java/io/File/libGetXSpace.c b/test/jdk/java/io/File/libGetXSpace.c index 59805e0adcb..9297721b8f4 100644 --- a/test/jdk/java/io/File/libGetXSpace.c +++ b/test/jdk/java/io/File/libGetXSpace.c @@ -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 @@ -85,7 +85,7 @@ Java_GetXSpace_getSpace0 BOOL hres = pfnGetDiskSpaceInformation(path, &diskSpaceInfo); (*env)->ReleaseStringChars(env, root, strchars); if (FAILED(hres)) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", "GetDiskSpaceInformationW"); return totalSpaceIsEstimated; } @@ -113,7 +113,7 @@ Java_GetXSpace_getSpace0 &totalNumberOfBytes, &totalNumberOfFreeBytes); (*env)->ReleaseStringChars(env, root, strchars); if (FAILED(hres)) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", "GetDiskFreeSpaceExW"); return totalSpaceIsEstimated; } @@ -131,8 +131,7 @@ Java_GetXSpace_getSpace0 char* chars = (char*)malloc((len + 1)*sizeof(char)); if (chars == NULL) { (*env)->ReleaseStringChars(env, root, strchars); - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", - "malloc"); + JNU_ThrowOutOfMemoryError(env, "malloc"); return JNI_FALSE; } @@ -146,7 +145,7 @@ Java_GetXSpace_getSpace0 int result = statfs(chars, &buf); free(chars); if (result < 0) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", strerror(errno)); return totalSpaceIsEstimated; } diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java similarity index 88% rename from test/jdk/java/security/Security/ConfigFileTest.java rename to test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java index caf657005e1..4cf723f856a 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.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 @@ -62,13 +62,13 @@ import java.util.stream.Stream; * @test * @summary Tests security properties passed through java.security, * java.security.properties or included from other properties files. - * @bug 8155246 8292297 8292177 8281658 8319332 + * @bug 4303068 8155246 8292297 8292177 8281658 8319332 * @modules java.base/sun.net.www * @library /test/lib - * @run main ConfigFileTest + * @run main ExtraFileAndIncludes */ -public class ConfigFileTest { +public class ExtraFileAndIncludes { static final String SEPARATOR_THIN = "----------------------------"; private static void printTestHeader(String testName) { @@ -91,7 +91,8 @@ public class ConfigFileTest { } else { // Executed by the test JVM. try (FilesManager filesMgr = new FilesManager()) { - for (Method m : ConfigFileTest.class.getDeclaredMethods()) { + for (Method m : + ExtraFileAndIncludes.class.getDeclaredMethods()) { if (m.getName().startsWith("test")) { printTestHeader(m.getName()); Executor.run(m, filesMgr); @@ -120,7 +121,7 @@ public class ConfigFileTest { static void testIncludeBasic(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.FILE_URI); PropsFile file0 = filesMgr.newFile("file0.properties"); PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); PropsFile file2 = filesMgr.newFile("dir1/dir2/file2.properties"); @@ -130,7 +131,7 @@ public class ConfigFileTest { file2.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.FILE_URI, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } @@ -152,7 +153,7 @@ public class ConfigFileTest { static void testIncludeWithOverrideAll(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); PropsFile file0 = filesMgr.newFile("file0.properties"); PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); @@ -160,40 +161,40 @@ public class ConfigFileTest { extraFile.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.setExtraFile(extraFile, true); ex.assertSuccess(); } static void extraPropertiesByHelper(Executor ex, FilesManager filesMgr, - Executor.ExtraMode mode) throws Exception { - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraMode mode) throws Exception { + ExtraPropsFile extraFile = filesMgr.newExtraFile(mode); PropsFile file0 = filesMgr.newFile("file0.properties"); extraFile.addRelativeInclude(file0); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, mode, true); + ex.setExtraFile(extraFile, true); ex.assertSuccess(); } static void testExtraPropertiesByPathAbsolute(Executor ex, FilesManager filesMgr) throws Exception { - extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_ABS); + extraPropertiesByHelper(ex, filesMgr, ExtraMode.PATH_ABS); } static void testExtraPropertiesByPathRelative(Executor ex, FilesManager filesMgr) throws Exception { - extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_REL); + extraPropertiesByHelper(ex, filesMgr, ExtraMode.PATH_REL); } static void specialCharsIncludes(Executor ex, FilesManager filesMgr, - char specialChar, Executor.ExtraMode extraMode, - boolean useRelativeIncludes) throws Exception { + char specialChar, ExtraMode extraMode, boolean useRelativeIncludes) + throws Exception { String suffix = specialChar + ".properties"; ExtraPropsFile extraFile; PropsFile file0, file1; try { - extraFile = filesMgr.newExtraFile("extra" + suffix); + extraFile = filesMgr.newExtraFile("extra" + suffix, extraMode); file0 = filesMgr.newFile("file0" + suffix); file1 = filesMgr.newFile("file1" + suffix); } catch (InvalidPathException ipe) { @@ -210,20 +211,18 @@ public class ConfigFileTest { extraFile.addAbsoluteInclude(file1); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, extraMode, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } static void testUnicodeIncludes1(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.PATH_ABS, true); + specialCharsIncludes(ex, filesMgr, '\u2022', ExtraMode.PATH_ABS, true); } static void testUnicodeIncludes2(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.FILE_URI, true); + specialCharsIncludes(ex, filesMgr, '\u2022', ExtraMode.FILE_URI, true); } static void testUnicodeIncludes3(Executor ex, FilesManager filesMgr) @@ -232,7 +231,7 @@ public class ConfigFileTest { // file:/tmp/extra•.properties are supported for the extra file. // However, relative includes are not allowed in these cases. specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.RAW_FILE_URI1, false); + ExtraMode.RAW_FILE_URI1, false); } static void testUnicodeIncludes4(Executor ex, FilesManager filesMgr) @@ -241,19 +240,17 @@ public class ConfigFileTest { // file:///tmp/extra•.properties are supported for the extra file. // However, relative includes are not allowed in these cases. specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.RAW_FILE_URI2, false); + ExtraMode.RAW_FILE_URI2, false); } static void testSpaceIncludes1(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.PATH_ABS, true); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.PATH_ABS, true); } static void testSpaceIncludes2(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.FILE_URI, true); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.FILE_URI, true); } static void testSpaceIncludes3(Executor ex, FilesManager filesMgr) @@ -261,8 +258,7 @@ public class ConfigFileTest { // Backward compatibility check. Malformed URLs such as // file:/tmp/extra .properties are supported for the extra file. // However, relative includes are not allowed in these cases. - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.RAW_FILE_URI1, false); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.RAW_FILE_URI1, false); } static void testSpaceIncludes4(Executor ex, FilesManager filesMgr) @@ -270,8 +266,7 @@ public class ConfigFileTest { // Backward compatibility check. Malformed URLs such as // file:///tmp/extra .properties are supported for the extra file. // However, relative includes are not allowed in these cases. - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.RAW_FILE_URI2, false); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.RAW_FILE_URI2, false); } static void notOverrideOnFailureHelper(Executor ex, FilesManager filesMgr, @@ -370,13 +365,13 @@ public class ConfigFileTest { static void testCannotResolveRelativeFromHTTPServed(Executor ex, FilesManager filesMgr) throws Exception { - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); PropsFile file0 = filesMgr.newFile("file0.properties"); extraFile.addRelativeInclude(file0); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.setExtraFile(extraFile, true); ex.assertError("InternalError: Cannot resolve '" + file0.fileName + "' relative path when included from a non-regular " + "properties file (e.g. HTTP served file)"); @@ -394,14 +389,15 @@ public class ConfigFileTest { masterFile.addRelativeInclude(file0); ex.setMasterFile(masterFile); - ex.assertError( - "InternalError: Cyclic include of '" + masterFile.path + "'"); + ex.assertError("Cyclic include"); + ex.getOutputAnalyzer().stderrShouldMatch("\\QInternalError: Cyclic " + + "include of '\\E[^']+\\Q" + masterFile.fileName + "'\\E"); } static void testCannotIncludeURL(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); masterFile.addRawProperty("include", extraFile.url.toString()); @@ -432,8 +428,7 @@ public class ConfigFileTest { // Launch a JDK without a master java.security file present, but with an // extra file passed. Since the "security.overridePropertiesFile=true" // security property is missing, it should fail anyway. - ex.setExtraFile( - filesMgr.newExtraFile(), Executor.ExtraMode.FILE_URI, true); + ex.setExtraFile(filesMgr.newExtraFile(ExtraMode.FILE_URI), true); ex.assertError("InternalError: Error loading java.security file"); } } @@ -455,17 +450,24 @@ sealed class PropsFile permits ExtraPropsFile { static Include of(PropsFile propsFile, String value) { return new Include(propsFile, value); } + + void assertProcessed(OutputAnalyzer oa) { + oa.shouldContain("processing include: '" + value + "'"); + oa.shouldContain("finished processing " + propsFile.displayPath); + } } protected final List includes = new ArrayList<>(); protected final PrintWriter writer; protected boolean includedFromExtra = false; + protected Path displayPath; final String fileName; final Path path; PropsFile(String fileName, Path path) throws IOException { this.fileName = fileName; this.path = path; + this.displayPath = path; this.writer = new PrintWriter(Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND), true); } @@ -513,8 +515,9 @@ sealed class PropsFile permits ExtraPropsFile { } void addRelativeInclude(PropsFile propsFile) { - addIncludeDefinition(Include.of(propsFile, - path.getParent().relativize(propsFile.path).toString())); + Path rel = path.getParent().relativize(propsFile.path); + addIncludeDefinition(Include.of(propsFile, rel.toString())); + propsFile.displayPath = displayPath.getParent().resolve(rel); } void assertApplied(OutputAnalyzer oa) { @@ -522,8 +525,7 @@ sealed class PropsFile permits ExtraPropsFile { FilesManager.APPLIED_PROP_VALUE); for (Include include : includes) { include.propsFile.assertApplied(oa); - oa.shouldContain("processing include: '" + include.value + "'"); - oa.shouldContain("finished processing " + include.propsFile.path); + include.assertProcessed(oa); } } @@ -534,8 +536,7 @@ sealed class PropsFile permits ExtraPropsFile { if (!include.propsFile.includedFromExtra) { include.propsFile.assertWasOverwritten(oa); } - oa.shouldContain("processing include: '" + include.value + "'"); - oa.shouldContain("finished processing " + include.propsFile.path); + include.assertProcessed(oa); } } @@ -556,13 +557,24 @@ sealed class PropsFile permits ExtraPropsFile { } } +enum ExtraMode { + HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL +} + final class ExtraPropsFile extends PropsFile { + private static final Path CWD = Path.of(".").toAbsolutePath(); private final Map systemProps = new LinkedHashMap<>(); + private final ExtraMode mode; final URI url; - ExtraPropsFile(String fileName, URI url, Path path) throws IOException { + ExtraPropsFile(String fileName, URI url, Path path, ExtraMode mode) + throws IOException { super(fileName, path); this.url = url; + this.mode = mode; + if (mode == ExtraMode.PATH_REL) { + this.displayPath = CWD.relativize(path); + } } @Override @@ -578,14 +590,25 @@ final class ExtraPropsFile extends PropsFile { super.addIncludeDefinition(include); } + String getSysPropValue() { + return switch (mode) { + case HTTP_SERVED -> url.toString(); + case FILE_URI -> path.toUri().toString(); + case RAW_FILE_URI1 -> "file:" + path; + case RAW_FILE_URI2 -> + "file://" + (path.startsWith("/") ? "" : "/") + path; + case PATH_ABS, PATH_REL -> displayPath.toString(); + }; + } + Map getSystemProperties() { return Collections.unmodifiableMap(systemProps); } } final class FilesManager implements Closeable { - private static final Path ROOT_DIR = - Path.of(ConfigFileTest.class.getSimpleName()).toAbsolutePath(); + private static final Path ROOT_DIR = Path.of( + ExtraFileAndIncludes.class.getSimpleName()).toAbsolutePath(); private static final Path PROPS_DIR = ROOT_DIR.resolve("properties"); private static final Path JDK_DIR = ROOT_DIR.resolve("jdk"); private static final Path MASTER_FILE = @@ -684,11 +707,11 @@ final class FilesManager implements Closeable { propsFile.addComment("Property to determine if this properties file " + "was parsed and not overwritten:"); propsFile.addRawProperty(fileName, APPLIED_PROP_VALUE); - propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment(ExtraFileAndIncludes.SEPARATOR_THIN); propsFile.addComment("Property to be overwritten by every properties " + "file (master, extra or included):"); propsFile.addRawProperty(LAST_FILE_PROP_NAME, fileName); - propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment(ExtraFileAndIncludes.SEPARATOR_THIN); createdFiles.add(propsFile); return propsFile; } @@ -702,16 +725,17 @@ final class FilesManager implements Closeable { return newFile(MASTER_FILE, PropsFile::new); } - ExtraPropsFile newExtraFile() throws IOException { - return newExtraFile("extra.properties"); + ExtraPropsFile newExtraFile(ExtraMode mode) throws IOException { + return newExtraFile("extra.properties", mode); } - ExtraPropsFile newExtraFile(String extraFileName) throws IOException { + ExtraPropsFile newExtraFile(String extraFileName, ExtraMode mode) + throws IOException { return (ExtraPropsFile) newFile(PROPS_DIR.resolve(extraFileName), (fileName, path) -> { URI uri = serverUri.resolve(ParseUtil.encodePath( ROOT_DIR.relativize(path).toString())); - return new ExtraPropsFile(fileName, uri, path); + return new ExtraPropsFile(fileName, uri, path, mode); }); } @@ -719,7 +743,7 @@ final class FilesManager implements Closeable { for (PropsFile propsFile : createdFiles) { System.err.println(); System.err.println(propsFile.path.toString()); - System.err.println(ConfigFileTest.SEPARATOR_THIN.repeat(3)); + System.err.println(ExtraFileAndIncludes.SEPARATOR_THIN.repeat(3)); try (Stream lines = Files.lines(propsFile.path)) { long lineNumber = 1L; Iterator it = lines.iterator(); @@ -757,9 +781,6 @@ final class FilesManager implements Closeable { } final class Executor { - enum ExtraMode { - HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL - } static final String RUNNER_ARG = "runner"; static final String INITIAL_PROP_LOG_MSG = "Initial security property: "; private static final String OVERRIDING_LOG_MSG = @@ -769,7 +790,6 @@ final class Executor { INITIAL_PROP_LOG_MSG + "postInitTest=shouldNotRecord", INITIAL_PROP_LOG_MSG + "include=", }; - private static final Path CWD = Path.of(".").toAbsolutePath(); private static final String JAVA_SEC_PROPS = "java.security.properties"; private static final String CLASS_PATH = Objects.requireNonNull( System.getProperty("test.classes"), "unspecified test.classes"); @@ -812,20 +832,10 @@ final class Executor { this.masterPropsFile = masterPropsFile; } - void setExtraFile(ExtraPropsFile extraPropsFile, ExtraMode mode, - boolean overrideAll) { + void setExtraFile(ExtraPropsFile extraPropsFile, boolean overrideAll) { this.extraPropsFile = extraPropsFile; expectedOverrideAll = overrideAll; - setRawExtraFile(switch (mode) { - case HTTP_SERVED -> extraPropsFile.url.toString(); - case FILE_URI -> extraPropsFile.path.toUri().toString(); - case RAW_FILE_URI1 -> "file:" + extraPropsFile.path; - case RAW_FILE_URI2 -> "file://" + - (extraPropsFile.path.startsWith("/") ? "" : "/") + - extraPropsFile.path; - case PATH_ABS -> extraPropsFile.path.toString(); - case PATH_REL -> CWD.relativize(extraPropsFile.path).toString(); - }, overrideAll); + setRawExtraFile(extraPropsFile.getSysPropValue(), overrideAll); } void setIgnoredExtraFile(String extraPropsFile, boolean overrideAll) { @@ -841,7 +851,7 @@ final class Executor { List command = new ArrayList<>(jvmArgs); Collections.addAll(command, Utils.getTestJavaOpts()); addSystemPropertiesAsJvmArgs(command); - command.add(ConfigFileTest.class.getSimpleName()); + command.add(ExtraFileAndIncludes.class.getSimpleName()); command.add(RUNNER_ARG); oa = ProcessTools.executeProcess(new ProcessBuilder(command)); oa.shouldHaveExitValue(successExpected ? 0 : 1); diff --git a/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java b/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java new file mode 100644 index 00000000000..7ca2a7c0f8b --- /dev/null +++ b/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Red Hat, Inc. + * + * 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 jdk.test.lib.process.ProcessTools; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; + +/* + * @test + * @summary Ensures the java executable is able to load extra security + * properties files from anonymous files and pipes. + * @bug 8352728 + * @requires os.family == "linux" + * @modules java.base/java.io:+open + * @library /test/lib + * @run main LinuxAnonymousFiles + */ + +public class LinuxAnonymousFiles { + private static final String TEST_PROP = "property.name=PROPERTY_VALUE"; + + private static final class AnonymousFile implements AutoCloseable { + public final Path fdPath; + private final FileInputStream fis; + + private AnonymousFile(CharSequence content) throws Exception { + Path tmp = Files.createTempFile("anonymous-file-", ""); + Files.writeString(tmp, content + System.lineSeparator()); + fis = new FileInputStream(tmp.toFile()); + Files.delete(tmp); + // Now the file is regular but anonymous, and will be unlinked + // when we close the last file descriptor referring to it. The + // fis instance ensures we keep it alive until close() is invoked. + Field field = FileDescriptor.class.getDeclaredField("fd"); + field.setAccessible(true); + int fd = field.getInt(fis.getFD()); + fdPath = Path.of("/proc/self").toRealPath().resolve("fd/" + fd); + } + + @Override + public void close() throws IOException { + fis.close(); + } + } + + public static void main(String[] args) throws Exception { + Path java = Path.of(System.getProperty("test.jdk"), "bin", "java"); + try (AnonymousFile af = new AnonymousFile("include /dev/stdin")) { + ProcessTools.executeProcess(new ProcessBuilder(java.toString(), + "-Djava.security.debug=properties", + "-Djava.security.properties=" + af.fdPath, + "-XshowSettings:security:properties", "-version"), + TEST_PROP).shouldHaveExitValue(0).shouldContain(TEST_PROP); + } + System.out.println("TEST PASS - OK"); + } +} diff --git a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file b/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file deleted file mode 100644 index 2b4c08c6903..00000000000 --- a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file +++ /dev/null @@ -1 +0,0 @@ -policy.url.2=file:${test.src}/SecurityPropFile.policy diff --git a/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java b/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java new file mode 100644 index 00000000000..a41fd2f3535 --- /dev/null +++ b/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Red Hat, Inc. + * + * 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 jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.FileUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; +import java.util.List; + +/* + * @test + * @summary Ensures java.security is loadable in Windows, even when the user + * does not have permissions on one of the parent directories. + * @bug 8352728 + * @requires os.family == "windows" + * @library /test/lib + * @run main WindowsParentDirPermissions + */ + +public class WindowsParentDirPermissions { + private static AutoCloseable restrictedAcl(Path path) throws IOException { + AclFileAttributeView view = + Files.getFileAttributeView(path, AclFileAttributeView.class); + List originalAcl = List.copyOf(view.getAcl()); + view.setAcl(List.of(AclEntry.newBuilder().setType(AclEntryType.DENY) + .setPrincipal(Files.getOwner(path)).build())); + return () -> view.setAcl(originalAcl); + } + + public static void main(String[] args) throws Exception { + Path temp = Files.createTempDirectory("JDK-8352728-tmp-"); + try (AutoCloseable a1 = () -> FileUtils.deleteFileTreeUnchecked(temp)) { + // Copy the jdk to a different directory + Path originalJdk = Path.of(System.getProperty("test.jdk")); + Path jdk = temp.resolve("jdk-parent-dir", "jdk"); + Files.createDirectories(jdk); + FileUtils.copyDirectory(originalJdk, jdk); + + // Remove current user permissions from jdk-parent-dir + try (AutoCloseable a2 = restrictedAcl(jdk.getParent())) { + // Make sure the permissions are affecting the current user + try { + jdk.toRealPath(); + throw new jtreg.SkippedException("Must run non-elevated!"); + } catch (IOException expected) { } + + // Execute the copied jdk, ensuring java.security.Security is + // loaded (i.e. use -XshowSettings:security:properties) + ProcessTools.executeProcess(new ProcessBuilder( + List.of(jdk.resolve("bin", "java.exe").toString(), + "-Djava.security.debug=properties", + "-XshowSettings:security:properties", + "-version"))).shouldHaveExitValue(0); + } + } + System.out.println("TEST PASS - OK"); + } +} diff --git a/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java b/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java index f96ab522517..cf138419c83 100644 --- a/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java +++ b/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.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 @@ -59,8 +59,11 @@ */ package tck.java.time; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.DateTimeException; import java.time.temporal.TemporalAccessor; @@ -68,8 +71,8 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.util.List; -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; import test.java.time.temporal.MockFieldNoValue; @@ -99,183 +102,137 @@ public abstract class AbstractDateTimeTest extends AbstractTCKTest { //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_isSupported_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - assertEquals(true, sample.isSupported(field), "Failed on " + sample + " " + field); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + assertTrue(sample.isSupported(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_isSupported_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - assertEquals(false, sample.isSupported(field), "Failed on " + sample + " " + field); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertFalse(sample.isSupported(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_isSupported_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - assertEquals(false, sample.isSupported(null), "Failed on " + sample); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_null(TemporalAccessor sample) { + assertFalse(sample.isSupported(null), "Failed on " + sample); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_range_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - sample.range(field); // no exception - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + assertDoesNotThrow(() -> sample.range(field)); } } - @Test() - public void basicTest_range_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.range(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.range(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_range_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.range(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.range(null), "Failed on " + sample); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_get_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - if (sample.range(field).isIntValue()) { - sample.get(field); // no exception - } else { - try { - sample.get(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + if (sample.range(field).isIntValue()) { + assertDoesNotThrow(() -> sample.get(field)); + } else { + assertThrows(DateTimeException.class, + () -> sample.get(field), "Failed on " + sample + " " + field); } } } - @Test() - public void basicTest_get_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.get(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.get(field), "Failed on " + sample + " " + field); } } - @Test - public void test_get_TemporalField_invalidField() { - Assertions.assertThrows(DateTimeException.class, () -> { - for (TemporalAccessor sample : samples()) { - sample.get(MockFieldNoValue.INSTANCE); - } - }); + @ParameterizedTest + @MethodSource("samples") + public void test_get_TemporalField_invalidField(TemporalAccessor sample) { + assertThrows(DateTimeException.class, + () -> sample.get(MockFieldNoValue.INSTANCE)); } - @Test() - public void basicTest_get_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.get(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.get(null), "Failed on " + sample); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_getLong_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - sample.getLong(field); // no exception - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + sample.getLong(field); } } - @Test() - public void basicTest_getLong_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.getLong(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.getLong(field), "Failed on " + sample + " " + field); } } - @Test - public void test_getLong_TemporalField_invalidField() { - Assertions.assertThrows(DateTimeException.class, () -> { - for (TemporalAccessor sample : samples()) { - sample.getLong(MockFieldNoValue.INSTANCE); - } - }); + @ParameterizedTest + @MethodSource("samples") + public void test_getLong_TemporalField_invalidField(TemporalAccessor sample) { + assertThrows(DateTimeException.class, + () -> sample.getLong(MockFieldNoValue.INSTANCE)); } - @Test() - public void basicTest_getLong_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.getLong(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.getLong(null), "Failed on " + sample); } //----------------------------------------------------------------------- - @Test - public void basicTest_query() { - for (TemporalAccessor sample : samples()) { - assertEquals("foo", sample.query(new TemporalQuery() { - @Override - public String queryFrom(TemporalAccessor temporal) { - return "foo"; - } - })); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_query(TemporalAccessor sample) { + assertEquals("foo", sample.query(new TemporalQuery() { + @Override + public String queryFrom(TemporalAccessor temporal) { + return "foo"; + } + })); } - } diff --git a/test/jdk/java/time/tck/java/time/TCKInstant.java b/test/jdk/java/time/tck/java/time/TCKInstant.java index 4a512d37665..8c245d69810 100644 --- a/test/jdk/java/time/tck/java/time/TCKInstant.java +++ b/test/jdk/java/time/tck/java/time/TCKInstant.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 @@ -139,7 +139,8 @@ public class TCKInstant extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_12345_123456789, Instant.MIN, Instant.MAX, Instant.EPOCH}; + TemporalAccessor[] array = {Instant.ofEpochSecond(12345, 123456789), + Instant.MIN, Instant.MAX, Instant.EPOCH}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKLocalDate.java b/test/jdk/java/time/tck/java/time/TCKLocalDate.java index 281007e7665..132aa31ed97 100644 --- a/test/jdk/java/time/tck/java/time/TCKLocalDate.java +++ b/test/jdk/java/time/tck/java/time/TCKLocalDate.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 @@ -170,7 +170,7 @@ public class TCKLocalDate extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2007_07_15, LocalDate.MAX, LocalDate.MIN, }; + TemporalAccessor[] array = {LocalDate.of(2007, 7, 15), LocalDate.MAX, LocalDate.MIN, }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKLocalTime.java b/test/jdk/java/time/tck/java/time/TCKLocalTime.java index bdec611ad76..e2fc4ea2ca7 100644 --- a/test/jdk/java/time/tck/java/time/TCKLocalTime.java +++ b/test/jdk/java/time/tck/java/time/TCKLocalTime.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 @@ -161,7 +161,8 @@ public class TCKLocalTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_12_30_40_987654321, LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; + TemporalAccessor[] array = {LocalTime.of(12, 30, 40, 987654321), + LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKMonthDay.java b/test/jdk/java/time/tck/java/time/TCKMonthDay.java index 84326a4c47a..36ad4af069e 100644 --- a/test/jdk/java/time/tck/java/time/TCKMonthDay.java +++ b/test/jdk/java/time/tck/java/time/TCKMonthDay.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 @@ -118,7 +118,7 @@ public class TCKMonthDay extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_07_15, }; + TemporalAccessor[] array = {MonthDay.of(7, 15), }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java index f2b98ee9972..6ba0318890f 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.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 @@ -167,7 +167,8 @@ public class TCKOffsetDateTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2008_6_30_11_30_59_000000500, OffsetDateTime.MIN, OffsetDateTime.MAX}; + TemporalAccessor[] array = {OffsetDateTime.of(2008, 6, 30, 11, 30, 59, 500, OFFSET_PONE), + OffsetDateTime.MIN, OffsetDateTime.MAX}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java index 350b21574fa..7ea9504edbc 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetTime.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 @@ -149,7 +149,8 @@ public class TCKOffsetTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_11_30_59_500_PONE, OffsetTime.MIN, OffsetTime.MAX}; + TemporalAccessor[] array = {OffsetTime.of(11, 30, 59, 500, OFFSET_PONE), + OffsetTime.MIN, OffsetTime.MAX}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKYearMonth.java b/test/jdk/java/time/tck/java/time/TCKYearMonth.java index 3f4fda2a924..e39bbe90cf9 100644 --- a/test/jdk/java/time/tck/java/time/TCKYearMonth.java +++ b/test/jdk/java/time/tck/java/time/TCKYearMonth.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 @@ -134,7 +134,7 @@ public class TCKYearMonth extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2008_06, }; + TemporalAccessor[] array = {YearMonth.of(2008, 6), }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java b/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java index 847035f121a..73adbbba88c 100644 --- a/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java +++ b/test/jdk/java/time/tck/java/time/TCKZonedDateTime.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 @@ -176,7 +176,7 @@ public class TCKZonedDateTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_DATE_TIME, }; + TemporalAccessor[] array = {ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 59, 500), ZONE_0100), }; return Arrays.asList(array); } diff --git a/test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java b/test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java new file mode 100644 index 00000000000..07d9b77f90e --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java @@ -0,0 +1,173 @@ +/* + * 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 4765299 + * @key headful + * @summary Verifies componentResized() is called with nested JSplitPanes + * @run main TestSplitPaneCompResize + */ + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.InputEvent; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; +import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; +import javax.swing.SwingUtilities; + +public class TestSplitPaneCompResize { + + private static JFrame frame; + private JSplitPane outer; + private static JButton leftOneTouchButton; + private static volatile Point leftBtnPos; + private static volatile boolean resized; + + public TestSplitPaneCompResize() { + + // set up a simple list embedded inside a scroll pane + String[] listItems = {"Item1", "Item2"}; + JList list = new JList<>(listItems); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.setSelectedIndex(0); + + JScrollPane comp = new JScrollPane(list); + JSplitPane inner = new JSplitPane(JSplitPane.VERTICAL_SPLIT, + comp, new JPanel()); + JPanel rightPanel = new JPanel(); + + outer = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + inner, rightPanel); + outer.setDividerLocation(150); + + //Provide minimum sizes for the two components in the split pane + Dimension minimumSize = new Dimension(100, 50); + comp.setMinimumSize(minimumSize); + inner.setMinimumSize(minimumSize); + rightPanel.setMinimumSize(minimumSize); + + //Provide a preferred size for the split pane + outer.setPreferredSize(new Dimension(400, 200)); + inner.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + System.out.println("inner resized"); + } + }); + comp.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + resized = true; + System.out.println("comp resized"); + } + }); + } + + public JSplitPane getSplitPane() { + return outer; + } + + + public static void main(String[] s) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("SplitPaneDemo"); + + TestSplitPaneCompResize sp = new TestSplitPaneCompResize(); + JSplitPane jsp = sp.getSplitPane(); + frame.getContentPane().add(jsp); + jsp.setUI(new MySplitPaneUI()); + jsp.setOneTouchExpandable(true); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + leftBtnPos = leftOneTouchButton.getLocationOnScreen(); + leftBtnPos.x += leftOneTouchButton.getWidth() / 2; + leftBtnPos.y += leftOneTouchButton.getHeight() / 2; + }); + + resized = false; + robot.mouseMove(leftBtnPos.x, leftBtnPos.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(1000); + + if (!resized) { + throw new RuntimeException("ComponentResized not called"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + + static class MySplitPaneUI extends BasicSplitPaneUI { + + public MySplitPaneUI() { + super(); + } + + public BasicSplitPaneDivider createDefaultDivider() { + return new MySplitPaneDivider(this); + } + } + + static class MySplitPaneDivider extends BasicSplitPaneDivider { + + public MySplitPaneDivider(BasicSplitPaneUI ui) { + super(ui); + } + + protected JButton createLeftOneTouchButton() { + leftOneTouchButton = super.createLeftOneTouchButton(); + return leftOneTouchButton; + } + + protected JButton createRightOneTouchButton() { + JButton rightOneTouchButton = super.createRightOneTouchButton(); + return rightOneTouchButton; + } + } +} diff --git a/test/jdk/javax/swing/Security/6657138/bug6657138.java b/test/jdk/javax/swing/Security/6657138/bug6657138.java deleted file mode 100644 index 4670d57595c..00000000000 --- a/test/jdk/javax/swing/Security/6657138/bug6657138.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. - * 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 6657138 - * @summary Verifies that buttons and labels don't share their ui's across appContexts - * @author Alexander Potochkin - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.*; -import javax.swing.plaf.ButtonUI; -import javax.swing.plaf.ComponentUI; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class bug6657138 implements Runnable { - - private static Map> componentMap = - Collections.synchronizedMap( - new HashMap>()); - - public void run() { - SunToolkit.createNewAppContext(); - try { - testUIMap(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void testUIMap() throws Exception { - UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); - Set components = componentMap.keySet(); - for (JComponent c : components) { - Map uiMap = componentMap.get(c); - - for (UIManager.LookAndFeelInfo laf : lafs) { - if ("Nimbus".equals(laf.getName())) { - // for some unclear reasons - // Nimbus ui delegate for a button is null - // when this method is called from the new AppContext - continue; - } - String className = laf.getClassName(); - try { - UIManager.setLookAndFeel(className); - } catch (final UnsupportedLookAndFeelException ignored) { - continue; - } - ComponentUI ui = UIManager.getUI(c); - if (ui == null) { - throw new RuntimeException("UI is null for " + c); - } - if (ui == uiMap.get(laf.getName())) { - throw new RuntimeException( - "Two AppContexts share the same UI delegate! \n" + - c + "\n" + ui); - } - uiMap.put(laf.getName(), ui); - } - } - } - - public static void main(String[] args) throws Exception { - componentMap.put(new JButton("JButton"), - new HashMap()); - componentMap.put(new JToggleButton("JToggleButton"), - new HashMap()); - componentMap.put(new JRadioButton("JRadioButton"), - new HashMap()); - componentMap.put(new JCheckBox("JCheckBox"), - new HashMap()); - componentMap.put(new JCheckBox("JLabel"), - new HashMap()); - testUIMap(); - ThreadGroup group = new ThreadGroup("6657138"); - Thread thread = new Thread(group, new bug6657138()); - thread.start(); - thread.join(); - } -} - diff --git a/test/jdk/jdk/classfile/InstructionValidationTest.java b/test/jdk/jdk/classfile/InstructionValidationTest.java index f02c7a9c78c..190cbffca55 100644 --- a/test/jdk/jdk/classfile/InstructionValidationTest.java +++ b/test/jdk/jdk/classfile/InstructionValidationTest.java @@ -195,6 +195,7 @@ class InstructionValidationTest { ensureFailFast(i, cob -> cob.iinc(i, 1)); } check(fails, () -> IncrementInstruction.of(i, 1)); + check(fails, () -> IncrementInstruction.of(IINC_W, i, 1)); check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i)); check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i)); check(fails, () -> LocalVariable.of(i, "test", CD_Object, dummyLabel, dummyLabel)); @@ -208,6 +209,7 @@ class InstructionValidationTest { check(fails, () -> LoadInstruction.of(u1Op, i)); for (var u1Op : List.of(ASTORE, ISTORE, LSTORE, FSTORE, DSTORE)) check(fails, () -> StoreInstruction.of(u1Op, i)); + check(fails, () -> IncrementInstruction.of(IINC, i, 1)); check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET, i)); } @@ -250,6 +252,13 @@ class InstructionValidationTest { IncrementInstruction.of(0, 2); IncrementInstruction.of(0, Short.MAX_VALUE); IncrementInstruction.of(0, Short.MIN_VALUE); + IncrementInstruction.of(IINC, 0, 2); + IncrementInstruction.of(IINC, 0, Byte.MIN_VALUE); + IncrementInstruction.of(IINC, 0, Byte.MAX_VALUE); + IncrementInstruction.of(IINC_W, 0, 2); + IncrementInstruction.of(IINC_W, 0, Short.MIN_VALUE); + IncrementInstruction.of(IINC_W, 0, Short.MAX_VALUE); + for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) { assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, i)); TestUtil.runCodeHandler(cob -> { @@ -257,6 +266,12 @@ class InstructionValidationTest { cob.return_(); }); } + for (int i : new int[] {Byte.MIN_VALUE - 1, Byte.MAX_VALUE + 1}) { + assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC, 0, i)); + } + for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) { + assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC_W, 0, i)); + } } @Test diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java index 3ad6d6f320f..fae7b678a09 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorTests.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 @@ -63,9 +63,21 @@ public class Byte128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void ANDReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::ANDReduce, Byte128VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::ORReduce, Byte128VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::XORReduce, Byte128VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::ADDReduce, Byte128VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void MULReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::MULReduce, Byte128VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void MINReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::MINReduce, Byte128VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void MAXReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::MAXReduce, Byte128VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void UMINReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::UMINReduce, Byte128VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void UMAXReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::UMAXReduce, Byte128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::FIRST_NONZEROReduce, Byte128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::SUADDReduce, Byte128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java index d64caa3424f..0e59db7d05d 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorTests.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 @@ -63,9 +63,21 @@ public class Byte256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void ANDReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::ANDReduce, Byte256VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::ORReduce, Byte256VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::XORReduce, Byte256VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::ADDReduce, Byte256VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void MULReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::MULReduce, Byte256VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void MINReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::MINReduce, Byte256VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void MAXReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::MAXReduce, Byte256VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void UMINReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::UMINReduce, Byte256VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void UMAXReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::UMAXReduce, Byte256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::FIRST_NONZEROReduce, Byte256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::SUADDReduce, Byte256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java index f494e3d3ae8..5ad3bbdbc05 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorTests.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 @@ -63,9 +63,21 @@ public class Byte512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void ANDReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::ANDReduce, Byte512VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::ORReduce, Byte512VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::XORReduce, Byte512VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::ADDReduce, Byte512VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void MULReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::MULReduce, Byte512VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void MINReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::MINReduce, Byte512VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void MAXReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::MAXReduce, Byte512VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void UMINReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::UMINReduce, Byte512VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void UMAXReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::UMAXReduce, Byte512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::FIRST_NONZEROReduce, Byte512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::SUADDReduce, Byte512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java index da2961cd97b..e28fb2b2001 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorTests.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 @@ -63,9 +63,21 @@ public class Byte64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void ANDReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::ANDReduce, Byte64VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::ORReduce, Byte64VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::XORReduce, Byte64VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::ADDReduce, Byte64VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void MULReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::MULReduce, Byte64VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void MINReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::MINReduce, Byte64VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void MAXReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::MAXReduce, Byte64VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void UMINReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::UMINReduce, Byte64VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void UMAXReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::UMAXReduce, Byte64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::FIRST_NONZEROReduce, Byte64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::SUADDReduce, Byte64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java index a5872219f10..b6932785b55 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.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 @@ -71,6 +71,19 @@ public class ByteMaxVectorTests extends AbstractVectorTest { private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3616,7 +3629,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3625,7 +3638,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3637,20 +3650,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void ANDReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3658,8 +3666,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::ANDReduce, ByteMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3669,7 +3700,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3683,20 +3714,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3705,7 +3731,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3714,7 +3740,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3729,17 +3755,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3747,8 +3768,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::ORReduce, ByteMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3758,7 +3802,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3775,17 +3819,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3794,7 +3833,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3803,7 +3842,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3818,17 +3857,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3836,8 +3870,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::XORReduce, ByteMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3847,7 +3904,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3864,17 +3921,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3883,7 +3935,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3892,7 +3944,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3907,17 +3959,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3925,8 +3972,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::ADDReduce, ByteMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3936,7 +4006,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3953,17 +4023,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3972,7 +4037,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3981,7 +4046,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3993,20 +4058,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void MULReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4014,8 +4074,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::MULReduce, ByteMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4025,7 +4108,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4039,20 +4122,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4061,7 +4139,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4070,7 +4148,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4082,20 +4160,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void MINReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4103,8 +4176,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::MINReduce, ByteMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4114,7 +4210,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4128,20 +4224,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4150,7 +4241,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4159,7 +4250,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4171,20 +4262,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void MAXReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4192,8 +4278,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::MAXReduce, ByteMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4203,7 +4312,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4217,20 +4326,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4239,7 +4343,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4248,7 +4352,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4260,20 +4364,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void UMINReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4281,8 +4380,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::UMINReduce, ByteMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4292,7 +4414,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4306,20 +4428,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4328,7 +4445,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4337,7 +4454,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4349,20 +4466,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void UMAXReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4370,8 +4482,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::UMAXReduce, ByteMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4381,7 +4516,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4395,20 +4530,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4417,7 +4547,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4426,7 +4556,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4438,20 +4568,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4459,8 +4584,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::FIRST_NONZEROReduce, ByteMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4470,7 +4618,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4484,20 +4632,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4554,7 +4697,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4563,7 +4706,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4578,17 +4721,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4596,8 +4734,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::SUADDReduce, ByteMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4607,7 +4768,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4623,17 +4784,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index 215edbb3a66..1d4cadd2158 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.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 @@ -62,6 +62,12 @@ public class Double128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double128VectorTests::MINReduce, Double128VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double128VectorTests::MAXReduce, Double128VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double128VectorTests::FIRST_NONZEROReduce, Double128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index 14d52320ce8..b5acfe0ef34 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.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 @@ -62,6 +62,12 @@ public class Double256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double256VectorTests::MINReduce, Double256VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double256VectorTests::MAXReduce, Double256VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double256VectorTests::FIRST_NONZEROReduce, Double256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index 91a4669a1b1..3f85d0e52a6 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.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 @@ -62,6 +62,12 @@ public class Double512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double512VectorTests::MINReduce, Double512VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double512VectorTests::MAXReduce, Double512VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double512VectorTests::FIRST_NONZEROReduce, Double512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index 659e18cd8af..ab3586fb424 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.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 @@ -62,6 +62,12 @@ public class Double64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double64VectorTests::MINReduce, Double64VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double64VectorTests::MAXReduce, Double64VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double64VectorTests::FIRST_NONZEROReduce, Double64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index ac85f0bd07b..8f135cd221a 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.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 @@ -68,6 +68,13 @@ public class DoubleMaxVectorTests extends AbstractVectorTest { private static final int Max = 256; // juts so we can do N/Max + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; + // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2389,7 +2396,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2398,7 +2405,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2413,17 +2420,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2431,8 +2433,31 @@ relativeError)); DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2442,7 +2467,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2459,17 +2484,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2478,7 +2498,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2487,7 +2507,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2499,20 +2519,15 @@ relativeError)); static void MULReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2520,8 +2535,31 @@ relativeError)); DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2531,7 +2569,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2545,20 +2583,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2567,7 +2600,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2576,7 +2609,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2588,20 +2621,15 @@ relativeError)); static void MINReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2609,8 +2637,31 @@ relativeError)); DoubleMaxVectorTests::MINReduce, DoubleMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2620,7 +2671,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2634,20 +2685,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2656,7 +2702,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2665,7 +2711,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2677,20 +2723,15 @@ relativeError)); static void MAXReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2698,8 +2739,31 @@ relativeError)); DoubleMaxVectorTests::MAXReduce, DoubleMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2709,7 +2773,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2723,20 +2787,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2745,7 +2804,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2754,7 +2813,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2766,20 +2825,15 @@ relativeError)); static void FIRST_NONZEROReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2787,8 +2841,31 @@ relativeError)); DoubleMaxVectorTests::FIRST_NONZEROReduce, DoubleMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2798,7 +2875,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2812,20 +2889,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index 32b93293fb9..f97c5b0064c 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.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 @@ -62,6 +62,12 @@ public class Float128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float128VectorTests::MINReduce, Float128VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float128VectorTests::MAXReduce, Float128VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float128VectorTests::FIRST_NONZEROReduce, Float128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index 9847865eacb..d9746d3291c 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.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 @@ -62,6 +62,12 @@ public class Float256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float256VectorTests::MINReduce, Float256VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float256VectorTests::MAXReduce, Float256VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float256VectorTests::FIRST_NONZEROReduce, Float256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index 0daa5310ff0..ca395221c6b 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.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 @@ -62,6 +62,12 @@ public class Float512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float512VectorTests::MINReduce, Float512VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float512VectorTests::MAXReduce, Float512VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float512VectorTests::FIRST_NONZEROReduce, Float512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index 921264fc4e2..1b14cdb7791 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.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 @@ -62,6 +62,12 @@ public class Float64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float64VectorTests::MINReduce, Float64VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float64VectorTests::MAXReduce, Float64VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float64VectorTests::FIRST_NONZEROReduce, Float64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index 07c9b7d0ef9..53edb408035 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.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 @@ -68,6 +68,13 @@ public class FloatMaxVectorTests extends AbstractVectorTest { private static final int Max = 256; // juts so we can do N/Max + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; + // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2400,7 +2407,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2409,7 +2416,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2424,17 +2431,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2442,8 +2444,31 @@ relativeError)); FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2453,7 +2478,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2470,17 +2495,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2489,7 +2509,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2498,7 +2518,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2510,20 +2530,15 @@ relativeError)); static void MULReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2531,8 +2546,31 @@ relativeError)); FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2542,7 +2580,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2556,20 +2594,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2578,7 +2611,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2587,7 +2620,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2599,20 +2632,15 @@ relativeError)); static void MINReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2620,8 +2648,31 @@ relativeError)); FloatMaxVectorTests::MINReduce, FloatMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2631,7 +2682,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2645,20 +2696,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2667,7 +2713,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2676,7 +2722,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2688,20 +2734,15 @@ relativeError)); static void MAXReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2709,8 +2750,31 @@ relativeError)); FloatMaxVectorTests::MAXReduce, FloatMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2720,7 +2784,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2734,20 +2798,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2756,7 +2815,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2765,7 +2824,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2777,20 +2836,15 @@ relativeError)); static void FIRST_NONZEROReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2798,8 +2852,31 @@ relativeError)); FloatMaxVectorTests::FIRST_NONZEROReduce, FloatMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2809,7 +2886,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2823,20 +2900,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int128VectorTests.java b/test/jdk/jdk/incubator/vector/Int128VectorTests.java index 93912243ecd..69bb0c84588 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorTests.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 @@ -63,9 +63,21 @@ public class Int128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void ANDReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::ANDReduce, Int128VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::ORReduce, Int128VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::XORReduce, Int128VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::ADDReduce, Int128VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void MULReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::MULReduce, Int128VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void MINReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::MINReduce, Int128VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void MAXReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::MAXReduce, Int128VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void UMINReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::UMINReduce, Int128VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void UMAXReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::UMAXReduce, Int128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::FIRST_NONZEROReduce, Int128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::SUADDReduce, Int128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int256VectorTests.java b/test/jdk/jdk/incubator/vector/Int256VectorTests.java index e8d6f3311ac..4e63de95b7b 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorTests.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 @@ -63,9 +63,21 @@ public class Int256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void ANDReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::ANDReduce, Int256VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::ORReduce, Int256VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::XORReduce, Int256VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::ADDReduce, Int256VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void MULReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::MULReduce, Int256VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void MINReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::MINReduce, Int256VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void MAXReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::MAXReduce, Int256VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void UMINReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::UMINReduce, Int256VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void UMAXReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::UMAXReduce, Int256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::FIRST_NONZEROReduce, Int256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::SUADDReduce, Int256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int512VectorTests.java b/test/jdk/jdk/incubator/vector/Int512VectorTests.java index 932d9ee4487..e2de7905a83 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorTests.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 @@ -63,9 +63,21 @@ public class Int512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void ANDReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::ANDReduce, Int512VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::ORReduce, Int512VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::XORReduce, Int512VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::ADDReduce, Int512VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void MULReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::MULReduce, Int512VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void MINReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::MINReduce, Int512VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void MAXReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::MAXReduce, Int512VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void UMINReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::UMINReduce, Int512VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void UMAXReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::UMAXReduce, Int512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::FIRST_NONZEROReduce, Int512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::SUADDReduce, Int512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int64VectorTests.java b/test/jdk/jdk/incubator/vector/Int64VectorTests.java index 3c00904a70e..d64db80b94d 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorTests.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 @@ -63,9 +63,21 @@ public class Int64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void ANDReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::ANDReduce, Int64VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::ORReduce, Int64VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::XORReduce, Int64VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::ADDReduce, Int64VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void MULReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::MULReduce, Int64VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void MINReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::MINReduce, Int64VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void MAXReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::MAXReduce, Int64VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void UMINReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::UMINReduce, Int64VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void UMAXReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::UMAXReduce, Int64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::FIRST_NONZEROReduce, Int64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::SUADDReduce, Int64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java index 42659bfe44e..7bf4dc48171 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.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 @@ -71,6 +71,19 @@ public class IntMaxVectorTests extends AbstractVectorTest { private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3660,7 +3673,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3669,7 +3682,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3681,20 +3694,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void ANDReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3702,8 +3710,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::ANDReduce, IntMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3713,7 +3744,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3727,20 +3758,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3749,7 +3775,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3758,7 +3784,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3773,17 +3799,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3791,8 +3812,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::ORReduce, IntMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3802,7 +3846,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3819,17 +3863,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3838,7 +3877,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3847,7 +3886,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3862,17 +3901,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3880,8 +3914,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::XORReduce, IntMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3891,7 +3948,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3908,17 +3965,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3927,7 +3979,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3936,7 +3988,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3951,17 +4003,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3969,8 +4016,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::ADDReduce, IntMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3980,7 +4050,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3997,17 +4067,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4016,7 +4081,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4025,7 +4090,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4037,20 +4102,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void MULReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4058,8 +4118,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::MULReduce, IntMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4069,7 +4152,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4083,20 +4166,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4105,7 +4183,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4114,7 +4192,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4126,20 +4204,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void MINReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4147,8 +4220,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::MINReduce, IntMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4158,7 +4254,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4172,20 +4268,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4194,7 +4285,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4203,7 +4294,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4215,20 +4306,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void MAXReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4236,8 +4322,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::MAXReduce, IntMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4247,7 +4356,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4261,20 +4370,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4283,7 +4387,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4292,7 +4396,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4304,20 +4408,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void UMINReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4325,8 +4424,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::UMINReduce, IntMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4336,7 +4458,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4350,20 +4472,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4372,7 +4489,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4381,7 +4498,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4393,20 +4510,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void UMAXReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4414,8 +4526,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::UMAXReduce, IntMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4425,7 +4560,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4439,20 +4574,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4461,7 +4591,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4470,7 +4600,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4482,20 +4612,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4503,8 +4628,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::FIRST_NONZEROReduce, IntMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4514,7 +4662,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4528,20 +4676,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4598,7 +4741,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4607,7 +4750,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4622,17 +4765,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4640,8 +4778,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::SUADDReduce, IntMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4651,7 +4812,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4667,17 +4828,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long128VectorTests.java b/test/jdk/jdk/incubator/vector/Long128VectorTests.java index 6c305a5c2b4..227f196ffdf 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorTests.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 @@ -63,9 +63,21 @@ public class Long128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void ANDReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::ANDReduce, Long128VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::ORReduce, Long128VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::XORReduce, Long128VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::ADDReduce, Long128VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void MULReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::MULReduce, Long128VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void MINReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::MINReduce, Long128VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void MAXReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::MAXReduce, Long128VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void UMINReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::UMINReduce, Long128VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void UMAXReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::UMAXReduce, Long128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::FIRST_NONZEROReduce, Long128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::SUADDReduce, Long128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long256VectorTests.java b/test/jdk/jdk/incubator/vector/Long256VectorTests.java index 3b276391cb7..c37e68e3728 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorTests.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 @@ -63,9 +63,21 @@ public class Long256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void ANDReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::ANDReduce, Long256VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::ORReduce, Long256VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::XORReduce, Long256VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::ADDReduce, Long256VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void MULReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::MULReduce, Long256VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void MINReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::MINReduce, Long256VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void MAXReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::MAXReduce, Long256VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void UMINReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::UMINReduce, Long256VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void UMAXReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::UMAXReduce, Long256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::FIRST_NONZEROReduce, Long256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::SUADDReduce, Long256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long512VectorTests.java b/test/jdk/jdk/incubator/vector/Long512VectorTests.java index 181116cf399..5f8abb5bdd5 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorTests.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 @@ -63,9 +63,21 @@ public class Long512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void ANDReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::ANDReduce, Long512VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::ORReduce, Long512VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::XORReduce, Long512VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::ADDReduce, Long512VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void MULReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::MULReduce, Long512VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void MINReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::MINReduce, Long512VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void MAXReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::MAXReduce, Long512VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void UMINReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::UMINReduce, Long512VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void UMAXReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::UMAXReduce, Long512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::FIRST_NONZEROReduce, Long512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::SUADDReduce, Long512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long64VectorTests.java b/test/jdk/jdk/incubator/vector/Long64VectorTests.java index cb052925778..5f8a9018384 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorTests.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 @@ -63,9 +63,21 @@ public class Long64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void ANDReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::ANDReduce, Long64VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::ORReduce, Long64VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::XORReduce, Long64VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::ADDReduce, Long64VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void MULReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::MULReduce, Long64VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void MINReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::MINReduce, Long64VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void MAXReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::MAXReduce, Long64VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void UMINReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::UMINReduce, Long64VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void UMAXReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::UMAXReduce, Long64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::FIRST_NONZEROReduce, Long64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::SUADDReduce, Long64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java index 6fe189867c2..17fee0a7765 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.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 @@ -71,6 +71,19 @@ public class LongMaxVectorTests extends AbstractVectorTest { private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3682,7 +3695,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3691,7 +3704,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3703,20 +3716,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void ANDReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3724,8 +3732,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::ANDReduce, LongMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3735,7 +3766,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3749,20 +3780,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3771,7 +3797,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3780,7 +3806,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3795,17 +3821,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3813,8 +3834,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::ORReduce, LongMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3824,7 +3868,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3841,17 +3885,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3860,7 +3899,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3869,7 +3908,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3884,17 +3923,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3902,8 +3936,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::XORReduce, LongMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3913,7 +3970,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3930,17 +3987,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3949,7 +4001,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3958,7 +4010,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3973,17 +4025,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3991,8 +4038,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::ADDReduce, LongMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -4002,7 +4072,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4019,17 +4089,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4038,7 +4103,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4047,7 +4112,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4059,20 +4124,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void MULReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4080,8 +4140,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::MULReduce, LongMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4091,7 +4174,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4105,20 +4188,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4127,7 +4205,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4136,7 +4214,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4148,20 +4226,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void MINReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4169,8 +4242,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::MINReduce, LongMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4180,7 +4276,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4194,20 +4290,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4216,7 +4307,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4225,7 +4316,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4237,20 +4328,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void MAXReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4258,8 +4344,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::MAXReduce, LongMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4269,7 +4378,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4283,20 +4392,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4305,7 +4409,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4314,7 +4418,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4326,20 +4430,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void UMINReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4347,8 +4446,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::UMINReduce, LongMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4358,7 +4480,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4372,20 +4494,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4394,7 +4511,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4403,7 +4520,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4415,20 +4532,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void UMAXReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4436,8 +4548,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::UMAXReduce, LongMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4447,7 +4582,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4461,20 +4596,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4483,7 +4613,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4492,7 +4622,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4504,20 +4634,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4525,8 +4650,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::FIRST_NONZEROReduce, LongMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4536,7 +4684,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4550,20 +4698,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4620,7 +4763,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4629,7 +4772,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4644,17 +4787,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4662,8 +4800,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::SUADDReduce, LongMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4673,7 +4834,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4689,17 +4850,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short128VectorTests.java b/test/jdk/jdk/incubator/vector/Short128VectorTests.java index be39a01a4e7..fb740fedfd4 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorTests.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 @@ -63,9 +63,21 @@ public class Short128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void ANDReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::ANDReduce, Short128VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::ORReduce, Short128VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::XORReduce, Short128VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::ADDReduce, Short128VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void MULReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::MULReduce, Short128VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void MINReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::MINReduce, Short128VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void MAXReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::MAXReduce, Short128VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void UMINReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::UMINReduce, Short128VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void UMAXReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::UMAXReduce, Short128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::FIRST_NONZEROReduce, Short128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.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]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::SUADDReduce, Short128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short256VectorTests.java b/test/jdk/jdk/incubator/vector/Short256VectorTests.java index 0b6ae53710e..cd6aa113b84 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorTests.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 @@ -63,9 +63,21 @@ public class Short256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void ANDReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::ANDReduce, Short256VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::ORReduce, Short256VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::XORReduce, Short256VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::ADDReduce, Short256VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void MULReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::MULReduce, Short256VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void MINReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::MINReduce, Short256VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void MAXReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::MAXReduce, Short256VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void UMINReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::UMINReduce, Short256VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void UMAXReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::UMAXReduce, Short256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::FIRST_NONZEROReduce, Short256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.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]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::SUADDReduce, Short256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short512VectorTests.java b/test/jdk/jdk/incubator/vector/Short512VectorTests.java index d4568068b6a..722f826f3e9 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorTests.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 @@ -63,9 +63,21 @@ public class Short512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void ANDReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::ANDReduce, Short512VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::ORReduce, Short512VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::XORReduce, Short512VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::ADDReduce, Short512VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void MULReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::MULReduce, Short512VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void MINReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::MINReduce, Short512VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void MAXReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::MAXReduce, Short512VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void UMINReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::UMINReduce, Short512VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void UMAXReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::UMAXReduce, Short512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::FIRST_NONZEROReduce, Short512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.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]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::SUADDReduce, Short512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short64VectorTests.java b/test/jdk/jdk/incubator/vector/Short64VectorTests.java index 83d5cfd18e8..9ec8ac08789 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorTests.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 @@ -63,9 +63,21 @@ public class Short64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void ANDReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::ANDReduce, Short64VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::ORReduce, Short64VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::XORReduce, Short64VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::ADDReduce, Short64VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void MULReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::MULReduce, Short64VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void MINReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::MINReduce, Short64VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void MAXReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::MAXReduce, Short64VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void UMINReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::UMINReduce, Short64VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void UMAXReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::UMAXReduce, Short64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::FIRST_NONZEROReduce, Short64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.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]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::SUADDReduce, Short64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java index 45ba8db51c1..ad2efd3575d 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.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 @@ -71,6 +71,19 @@ public class ShortMaxVectorTests extends AbstractVectorTest { private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3607,7 +3620,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3616,7 +3629,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3628,20 +3641,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void ANDReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3649,8 +3657,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::ANDReduce, ShortMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3660,7 +3691,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3674,20 +3705,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3696,7 +3722,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3705,7 +3731,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3720,17 +3746,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3738,8 +3759,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::ORReduce, ShortMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3749,7 +3793,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3766,17 +3810,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3785,7 +3824,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3794,7 +3833,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3809,17 +3848,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3827,8 +3861,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::XORReduce, ShortMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3838,7 +3895,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3855,17 +3912,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3874,7 +3926,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3883,7 +3935,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3898,17 +3950,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3916,8 +3963,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::ADDReduce, ShortMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3927,7 +3997,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3944,17 +4014,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3963,7 +4028,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3972,7 +4037,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3984,20 +4049,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void MULReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4005,8 +4065,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::MULReduce, ShortMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (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]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4016,7 +4099,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4030,20 +4113,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4052,7 +4130,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4061,7 +4139,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4073,20 +4151,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void MINReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4094,8 +4167,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::MINReduce, ShortMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4105,7 +4201,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4119,20 +4215,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4141,7 +4232,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4150,7 +4241,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4162,20 +4253,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void MAXReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4183,8 +4269,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::MAXReduce, ShortMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.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]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4194,7 +4303,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4208,20 +4317,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4230,7 +4334,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4239,7 +4343,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4251,20 +4355,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void UMINReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4272,8 +4371,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::UMINReduce, ShortMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4283,7 +4405,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4297,20 +4419,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4319,7 +4436,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4328,7 +4445,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4340,20 +4457,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void UMAXReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4361,8 +4473,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::UMAXReduce, ShortMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4372,7 +4507,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4386,20 +4521,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4408,7 +4538,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4417,7 +4547,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4429,20 +4559,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4450,8 +4575,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::FIRST_NONZEROReduce, ShortMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.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]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4461,7 +4609,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4475,20 +4623,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 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()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4545,7 +4688,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4554,7 +4697,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4569,17 +4712,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4587,8 +4725,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::SUADDReduce, ShortMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4598,7 +4759,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4614,17 +4775,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/gen-template.sh b/test/jdk/jdk/incubator/vector/gen-template.sh index d9bae116da2..d2528b13b78 100644 --- a/test/jdk/jdk/incubator/vector/gen-template.sh +++ b/test/jdk/jdk/incubator/vector/gen-template.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# 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 @@ -511,23 +511,23 @@ gen_binary_bcst_op_no_masked "MAX+max" "Math.max(a, b)" gen_saturating_binary_op_associative "SUADD" "VectorMath.addSaturatingUnsigned(a, b)" "BITWISE" # Reductions. -gen_reduction_op "AND" "\&" "BITWISE" "-1" -gen_reduction_op "OR" "|" "BITWISE" "0" -gen_reduction_op "XOR" "^" "BITWISE" "0" -gen_reduction_op "ADD" "+" "" "0" -gen_reduction_op "MUL" "*" "" "1" -gen_reduction_op_func "MIN" "(\$type\$) Math.min" "" "\$Wideboxtype\$.\$MaxValue\$" -gen_reduction_op_func "MAX" "(\$type\$) Math.max" "" "\$Wideboxtype\$.\$MinValue\$" -gen_reduction_op_func "UMIN" "(\$type\$) VectorMath.minUnsigned" "BITWISE" "\$Wideboxtype\$.\$MaxValue\$" -gen_reduction_op_func "UMAX" "(\$type\$) VectorMath.maxUnsigned" "BITWISE" "\$Wideboxtype\$.\$MinValue\$" -gen_reduction_op_func "FIRST_NONZERO" "firstNonZero" "" "(\$type\$) 0" +gen_reduction_op "AND" "\&" "BITWISE" "AND_IDENTITY" +gen_reduction_op "OR" "|" "BITWISE" "OR_IDENTITY" +gen_reduction_op "XOR" "^" "BITWISE" "XOR_IDENTITY" +gen_reduction_op "ADD" "+" "" "ADD_IDENTITY" +gen_reduction_op "MUL" "*" "" "MUL_IDENTITY" +gen_reduction_op_func "MIN" "(\$type\$) Math.min" "" "MIN_IDENTITY" +gen_reduction_op_func "MAX" "(\$type\$) Math.max" "" "MAX_IDENTITY" +gen_reduction_op_func "UMIN" "(\$type\$) VectorMath.minUnsigned" "BITWISE" "UMIN_IDENTITY" +gen_reduction_op_func "UMAX" "(\$type\$) VectorMath.maxUnsigned" "BITWISE" "UMAX_IDENTITY" +gen_reduction_op_func "FIRST_NONZERO" "firstNonZero" "" "FIRST_NONZERO_IDENTITY" # Boolean reductions. gen_bool_reduction_op "anyTrue" "|" "BITWISE" "false" gen_bool_reduction_op "allTrue" "\&" "BITWISE" "true" # Saturating reductions. -gen_saturating_reduction_op "SUADD" "(\$type\$) VectorMath.addSaturatingUnsigned" "BITWISE" "0" +gen_saturating_reduction_op "SUADD" "(\$type\$) VectorMath.addSaturatingUnsigned" "BITWISE" "SUADD_IDENTITY" #Insert gen_with_op "withLane" "" "" "" diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template index bf03a4d0430..73e02bf68dd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template @@ -2,20 +2,15 @@ $type$[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); - $type$ ra = [[TEST_INIT]]; - - 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); - r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]], vmask)); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]], vmask); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template index f108491523e..82e20d594b6 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template @@ -2,20 +2,15 @@ $type$[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); - $type$ ra = [[TEST_INIT]]; - - 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); - r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra [[TEST_OP]]= av.reduceLanes(VectorOperators.[[TEST]], vmask); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]], vmask); + r[i] = v; + ra [[TEST_OP]]= v; } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template index 7082ff0795e..94fae420cdd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template @@ -1,19 +1,14 @@ $type$[] a = fa.apply(SPECIES.length()); $type$[] r = fr.apply(SPECIES.length()); - $type$ ra = [[TEST_INIT]]; - - 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); - r[i] = av.reduceLanes(VectorOperators.[[TEST]]); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]])); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]]); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template index 66250c09213..3edc2b27e3e 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template @@ -1,19 +1,14 @@ $type$[] a = fa.apply(SPECIES.length()); $type$[] r = fr.apply(SPECIES.length()); - $type$ ra = [[TEST_INIT]]; - - 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); - r[i] = av.reduceLanes(VectorOperators.[[TEST]]); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra [[TEST_OP]]= av.reduceLanes(VectorOperators.[[TEST]]); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]]); + r[i] = v; + ra [[TEST_OP]]= v; } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template index bf03a4d0430..73e02bf68dd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template @@ -2,20 +2,15 @@ $type$[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); - $type$ ra = [[TEST_INIT]]; - - 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); - r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]], vmask)); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]], vmask); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template index 7082ff0795e..94fae420cdd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template @@ -1,19 +1,14 @@ $type$[] a = fa.apply(SPECIES.length()); $type$[] r = fr.apply(SPECIES.length()); - $type$ ra = [[TEST_INIT]]; - - 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); - r[i] = av.reduceLanes(VectorOperators.[[TEST]]); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]])); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]]); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template index e6bcdb83c2e..c797ad907fb 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template @@ -5,3 +5,26 @@ assertReductionArraysEquals(r, ra, a, $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); } + + @Test(dataProvider = "$type$UnaryOpProvider") + static void [[TEST]]ReduceIdentityValueTests(IntFunction<$type$[]> fa) { + $type$[] a = fa.apply(SPECIES.length()); + $type$ id = [[TEST_INIT]]; + + Assert.assertEquals([[TEST_OP]](id, id), id, + "[[TEST]]([[TEST_INIT]], [[TEST_INIT]]) != [[TEST_INIT]]"); + + $type$ x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals([[TEST_OP]](id, x), x); + Assert.assertEquals([[TEST_OP]](x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals([[TEST_OP]](id, x), x, + "[[TEST]]([[TEST_INIT]], " + x + ") != " + x); + Assert.assertEquals([[TEST_OP]](x, id), x, + "[[TEST]](" + x + ", [[TEST_INIT]]) != " + x); + } + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template index 5638940045d..f8438fa58c8 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template @@ -9,3 +9,26 @@ $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); #end[FP] } + + @Test(dataProvider = "$type$UnaryOpProvider") + static void [[TEST]]ReduceIdentityValueTests(IntFunction<$type$[]> fa) { + $type$[] a = fa.apply(SPECIES.length()); + $type$ id = [[TEST_INIT]]; + + Assert.assertEquals(($type$) (id [[TEST_OP]] id), id, + "[[TEST]]([[TEST_INIT]], [[TEST_INIT]]) != [[TEST_INIT]]"); + + $type$ x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(($type$) (id [[TEST_OP]] x), x); + Assert.assertEquals(($type$) (x [[TEST_OP]] id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(($type$) (id [[TEST_OP]] x), x, + "[[TEST]]([[TEST_INIT]], " + x + ") != " + x); + Assert.assertEquals(($type$) (x [[TEST_OP]] id), x, + "[[TEST]](" + x + ", [[TEST_INIT]]) != " + x); + } + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template index fdd9e47167e..d961a29e5c8 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template @@ -5,3 +5,26 @@ assertReductionArraysEquals(r, ra, a, $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); } + + @Test(dataProvider = "$type$SaturatingUnaryOpProvider") + static void [[TEST]]ReduceIdentityValueTests(IntFunction<$type$[]> fa) { + $type$[] a = fa.apply(SPECIES.length()); + $type$ id = [[TEST_INIT]]; + + Assert.assertEquals([[TEST_OP]](id, id), id, + "[[TEST]]([[TEST_INIT]], [[TEST_INIT]]) != [[TEST_INIT]]"); + + $type$ x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals([[TEST_OP]](id, x), x); + Assert.assertEquals([[TEST_OP]](x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals([[TEST_OP]](id, x), x, + "[[TEST]]([[TEST_INIT]], " + x + ") != " + x); + Assert.assertEquals([[TEST_OP]](x, id), x, + "[[TEST]](" + x + ", [[TEST_INIT]]) != " + x); + } + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index 7e00cb1678c..e1ec6624022 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -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 @@ -86,19 +86,37 @@ public class $vectorteststype$ extends AbstractVectorTest { #end[MaxBit] static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - #if[MaxBit] + static VectorShape getMaxBit() { return VectorShape.S_Max_BIT; } private static final int Max = 256; // juts so we can do N/$bits$ #end[MaxBit] - #if[BITWISE] + private static final $type$ CONST_SHIFT = $Boxtype$.SIZE / 2; #end[BITWISE] + + // Identity values for reduction operations + private static final $type$ ADD_IDENTITY = ($type$)0; +#if[BITWISE] + 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; +#if[BITWISE] + private static final $type$ OR_IDENTITY = ($type$)0; + private static final $type$ SUADD_IDENTITY = ($type$)0; + private static final $type$ UMAX_IDENTITY = ($type$)0; // Minimum unsigned value + private static final $type$ UMIN_IDENTITY = ($type$)-1; // Maximum unsigned value + private static final $type$ XOR_IDENTITY = ($type$)0; +#end[BITWISE] #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; diff --git a/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java b/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java index dbcb30255a8..b86593d84ba 100644 --- a/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java +++ b/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java @@ -24,6 +24,7 @@ /** * @test * @requires vm.flagless + * @requires test.thread.factory == null * @library /test/lib * @modules java.base/jdk.internal.misc * jdk.zipfs diff --git a/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java b/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java index d540acd853b..b3630fa7f77 100644 --- a/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java +++ b/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.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. * Copyright (c) 2025, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -98,8 +98,8 @@ public class TestEmergencyDumpAtOOM { // Check OldObjectSample events if (oldObjects.get() > 0L) { if (shouldCrash) { - Asserts.assertEquals("VM Error", shutdownReason.get()); - Asserts.assertEquals("Out of Memory", dumpReason.get()); + Asserts.assertEquals("CrashOnOutOfMemoryError", shutdownReason.get()); + Asserts.assertEquals("CrashOnOutOfMemoryError", dumpReason.get()); } else { Asserts.assertEquals("No remaining non-daemon Java threads", shutdownReason.get()); } diff --git a/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java b/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java index 147caee82ea..fbef91e73aa 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java +++ b/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.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 @@ -34,8 +34,9 @@ import jdk.jfr.Configuration; import jdk.jfr.Event; import jdk.jfr.Recording; import jdk.jfr.StackTrace; +import jdk.jfr.consumer.EventStream; import jdk.jfr.consumer.RecordedClassLoader; -import jdk.jfr.consumer.RecordingStream; +import jdk.test.lib.jfr.TestClassLoader; /** * @test @@ -52,28 +53,17 @@ public class TestBackToBackSensitive { static class FillEvent extends Event { String message; } + public static Object OBJECT; public static void main(String... arg) throws Exception { - Set threadDumps = Collections.synchronizedSet(new LinkedHashSet<>()); - Set classLoaderStatistics = Collections.synchronizedSet(new LinkedHashSet<>()); - Set physicalMemory = Collections.synchronizedSet(new LinkedHashSet<>()); - + TestClassLoader loader = new TestClassLoader(); + Class clazz = loader.loadClass(TestBackToBackSensitive.class.getName()); + String classLoaderName = loader.getClass().getName(); + OBJECT = clazz.getDeclaredConstructor().newInstance(); Configuration configuration = Configuration.getConfiguration("default"); - try (RecordingStream r1 = new RecordingStream(configuration)) { - r1.setMaxSize(Long.MAX_VALUE); - r1.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime())); - r1.onEvent("jdk.ClassLoaderStatistics", e -> { - RecordedClassLoader cl = e.getValue("classLoader"); - if (cl != null) { - if (cl.getType().getName().contains("PlatformClassLoader")) { - classLoaderStatistics.add(e.getStartTime()); - System.out.println("Class loader" + e); - } - } - }); - r1.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime())); + try (Recording r1 = new Recording(configuration)) { // Start chunk 1 - r1.startAsync(); + r1.start(); try (Recording r2 = new Recording()) { // Start chunk 2 r2.start(); @@ -86,6 +76,25 @@ public class TestBackToBackSensitive { f.commit(); } r1.stop(); + Path file = Path.of("file.jfr"); + r1.dump(file); + Set threadDumps = new LinkedHashSet<>(); + Set classLoaderStatistics = new LinkedHashSet<>(); + Set physicalMemory = new LinkedHashSet<>(); + try (EventStream es = EventStream.openFile(file)) { + es.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime())); + es.onEvent("jdk.ClassLoaderStatistics", e -> { + RecordedClassLoader cl = e.getValue("classLoader"); + if (cl != null) { + if (cl.getType().getName().equals(classLoaderName)) { + classLoaderStatistics.add(e.getStartTime()); + System.out.println("Class loader" + e); + } + } + }); + es.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime())); + es.start(); + } long chunkFiles = filesInRepository(); System.out.println("Number of chunk files: " + chunkFiles); // When jdk.PhysicalMemory is expected to be emitted: @@ -93,15 +102,15 @@ public class TestBackToBackSensitive { // Chunk 2: begin, end // Chunk 3: begin, end // Chunk 4: begin, end - assertCount(r1, "jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles); + assertCount("jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles); // When jdk.ClassLoaderStatistics and jdk.ThreadThreadDump are expected to be // emitted: // Chunk 1: begin, end // Chunk 2: begin, end // Chunk 3: end // Chunk 4: end - assertCount(r1, "jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2)); - assertCount(r1, "jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2)); + assertCount("jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2)); + assertCount("jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2)); } } @@ -110,15 +119,13 @@ public class TestBackToBackSensitive { return Files.list(repository).filter(p -> p.toString().endsWith(".jfr")).count(); } - private static void assertCount(RecordingStream stream, String eventName, Set timestamps, long expected) throws Exception { + private static void assertCount(String eventName, Set timestamps, long expected) throws Exception { System.out.println("Timestamps for " + eventName + ":"); for (Instant timestamp : timestamps) { System.out.println(timestamp); } int count = timestamps.size(); if (count != expected) { - System.out.println("Dumping failure file."); - stream.dump(Path.of("failure.jfr")); throw new Exception("Expected " + expected + " timestamps for event " + eventName + ", but got " + count); } } diff --git a/test/jdk/jdk/jfr/event/tracing/TestConstructors.java b/test/jdk/jdk/jfr/event/tracing/TestConstructors.java new file mode 100644 index 00000000000..26548646b49 --- /dev/null +++ b/test/jdk/jdk/jfr/event/tracing/TestConstructors.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.jfr.event.tracing; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that constructors are instrumented correctly. + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm -Xlog:jfr+methodtrace=debug + * jdk.jfr.event.tracing.TestConstructors + **/ +public class TestConstructors { + static private void methodThatThrows() { + throw new RuntimeException(); + } + + public static class Cat { + Cat() { + new String(); + methodThatThrows(); + super(); + methodThatThrows(); + } + } + + public static class Dog { + Dog() { + super(); + methodThatThrows(); + } + } + + public static class Tiger { + Tiger() { + methodThatThrows(); + super(); + } + } + + public static class Zebra { + Zebra(boolean shouldThrow) { + this(shouldThrow ? 1 : 0); + } + + Zebra(int shouldThrow) { + if (shouldThrow == 1) { + throw new RuntimeException(); + } + } + } + + public static class Snake { + Snake() { + try { + throw new RuntimeException(); + } catch (Exception e) { + // Ignore + } + super(); + } + } + + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + r.enable("jdk.MethodTrace").with("filter", Dog.class.getName() + ";" + Cat.class.getName() + ";" + Tiger.class.getName() + ";" + Zebra.class.getName() + ";" + Snake.class.getName()); + r.start(); + try { + new Cat(); + } catch (Exception e) { + // ignore + } + try { + new Dog(); + } catch (Exception e) { + // ignore + } + try { + new Tiger(); + } catch (Exception e) { + // ignore + } + try { + new Zebra(true); + } catch (Exception e) { + // ignore + } + try { + new Zebra(false); + } catch (Exception e) { + // ignore + } + try { + new Snake(); + } catch (Exception e) { + // ignore + } + r.stop(); + List events = Events.fromRecording(r); + var methods = buildMethodMap(events); + if (methods.size() != 5) { + throw new Exception("Expected 5 different methods"); + } + assertMethodCount(methods, "Cat", 1); + assertMethodCount(methods, "Dog", 1); + assertMethodCount(methods, "Snake", 1); + assertMethodCount(methods, "Tiger", 1); + assertMethodCount(methods, "Zebra", 3); + } + } + + private static void assertMethodCount(Map methods, String className, int expectedCount) throws Exception { + String name = TestConstructors.class.getName() + "$" + className + "::"; + Long count = methods.get(name); + if (count == null) { + throw new Exception("Could not find traced method " + name); + } + if (count != expectedCount) { + throw new Exception("Expected " + expectedCount + " trace event for " + name); + } + } + + private static Map buildMethodMap(List events) { + Map map = new TreeMap<>(); + for (RecordedEvent e : events) { + RecordedMethod m = e.getValue("method"); + String name = m.getType().getName() + "::" + m.getName(); + map.compute(name, (_, value) -> (value == null) ? 1 : value + 1); + } + for (var e : map.entrySet()) { + System.out.println(e.getKey() + " " + e.getValue()); + } + return map; + } +} \ No newline at end of file diff --git a/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java b/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java index 834d4ab4989..5709c95812a 100644 --- a/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java +++ b/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java @@ -93,6 +93,8 @@ public class TestInstrumentation { assertMethod(map, "exception", 2); assertMethod(map, "switchExpression", 3); assertMethod(map, "recursive", 4); + assertMethod(map, "deepException", 1); + assertMethod(map, "whileTrue", 1); assertMethod(map, "multipleReturns", 5); if (!map.isEmpty()) { throw new Exception("Found unexpected methods " + map.keySet()); @@ -105,6 +107,8 @@ public class TestInstrumentation { assertMethod(map, "exception", 2); assertMethod(map, "switchExpression", 3); assertMethod(map, "recursive", 4); + assertMethod(map, "deepException", 1); + assertMethod(map, "whileTrue", 1); assertMethod(map, "multipleReturns", 5); for (var entry : map.entrySet()) { long invocations = entry.getValue(); diff --git a/test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java b/test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java new file mode 100644 index 00000000000..b2c134e09b2 --- /dev/null +++ b/test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java @@ -0,0 +1,223 @@ +/* + * 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 8374377 + * @summary This test confirms the PNGImageProducer decodes 8-bit interlaced + * and non-interlaced PNGs correctly. + */ + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * The proposed change for 8374377 affects how 8-bit PNGs are decoded. + * So this test confirms that 8-bit PNGs (both interlaced and non-interlaced) + * are still decoded by the PNGImageDecoder so they match what ImageIO decodes. + * + * This test has never failed. + */ +public class PngImageDecoder8BitTest { + + static BufferedImage createBufferedImage(Image img) + throws ExecutionException, InterruptedException { + CompletableFuture future = new CompletableFuture<>(); + img.getSource().startProduction(new ImageConsumer() { + private int imageWidth, imageHeight; + private BufferedImage bi; + + @Override + public void setDimensions(int width, int height) { + imageWidth = width; + imageHeight = height; + } + + @Override + public void setProperties(Hashtable props) { + // intentionally empty + } + + @Override + public void setColorModel(ColorModel model) { + // intentionally empty + } + + @Override + public void setHints(int hintflags) { + // intentionally empty + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) { + if (bi == null) { + bi = new BufferedImage(imageWidth, imageHeight, + BufferedImage.TYPE_BYTE_INDEXED, + (IndexColorModel) model); + } + + if (w == imageWidth && h == imageHeight) { + // this is how interlaced PNGs are decoded: + bi.getRaster().setDataElements(0, 0, + imageWidth, imageHeight, pixels); + return; + } + + if (h != 1) { + throw new UnsupportedOperationException( + "this test requires h = 1"); + } + if (off != 0) { + throw new UnsupportedOperationException( + "this test requires off = 0"); + } + + bi.getRaster().setDataElements(x, y, w, 1, pixels); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) { + throw new UnsupportedOperationException(); + } + + @Override + public void imageComplete(int status) { + future.complete(bi); + } + }); + return future.get(); + } + + public static void main(String[] args) throws Exception { + BufferedImage expected = createImageData(); + for (boolean interlace : new boolean[] { false, true} ) { + System.out.println("Testing interlacing = "+ interlace); + byte[] imageData = encodePNG(expected, interlace); + + Image i = Toolkit.getDefaultToolkit().createImage(imageData); + BufferedImage actual = createBufferedImage(i); + + testCorrectness(expected, actual); + } + System.out.println("Confirmed that 8-bit PNGs decode correctly " + + "whether we use interlacing or not."); + } + + /** + * Create a large sample image stored as an 8-bit PNG. + */ + private static BufferedImage createImageData() { + BufferedImage bi = new BufferedImage(6000, 6000, + BufferedImage.TYPE_BYTE_INDEXED); + Random r = new Random(0); + Graphics2D g = bi.createGraphics(); + for (int a = 0; a < 20000; a++) { + g.setColor(new Color(r.nextInt(0xffffff))); + int radius = 10 + r.nextInt(90); + g.fillOval(r.nextInt(bi.getWidth()), r.nextInt(bi.getHeight()), + radius, radius); + } + g.dispose(); + return bi; + } + + /** + * Encode an image as 8-bit PNG. + */ + private static byte[] encodePNG(BufferedImage bi, boolean interlace) + throws IOException { + Iterator writers = + ImageIO.getImageWritersByFormatName("png"); + if (!writers.hasNext()) { + throw new IllegalStateException("No PNG writers found"); + } + ImageWriter writer = writers.next(); + + ImageWriteParam param = writer.getDefaultWriteParam(); + if (interlace) { + param.setProgressiveMode(ImageWriteParam.MODE_DEFAULT); + } + + try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + ImageOutputStream imageOut = + ImageIO.createImageOutputStream(byteOut)) { + writer.setOutput(imageOut); + writer.write(null, new IIOImage(bi, null, null), param); + return byteOut.toByteArray(); + } finally { + writer.dispose(); + } + } + + /** + * This throws an Error if the two images are not identical. + *

    + * This unit test is intended to accompany a performance enhancement for + * PNGImageDecoder. This method makes sure the enhancement didn't cost us + * any accuracy. + */ + private static void testCorrectness(BufferedImage expected, + BufferedImage actual) { + if (expected.getWidth() != actual.getWidth()) { + throw new RuntimeException("expected.getWidth() = " + + expected.getWidth() + ", actual.getWidth() = " + + actual.getWidth()); + } + if (expected.getHeight() != actual.getHeight()) { + throw new RuntimeException("expected.getHeight() = " + + expected.getHeight() + ", actual.getHeight() = " + + actual.getHeight()); + } + for (int y = 0; y < expected.getHeight(); y++) { + for (int x = 0; x < expected.getWidth(); x++) { + int argb1 = expected.getRGB(x, y); + int argb2 = actual.getRGB(x, y); + if (argb1 != argb2) { + throw new RuntimeException("x = " + x + ", y = " + y + + " argb1 = " + Integer.toUnsignedString(argb1, 16) + + " argb2 = " + Integer.toUnsignedString(argb2, 16)); + } + } + } + } +} diff --git a/test/jdk/sun/security/ec/ECDSAPrimitive.java b/test/jdk/sun/security/ec/ECDSAPrimitive.java index 71e2e30044b..b8901a784ff 100644 --- a/test/jdk/sun/security/ec/ECDSAPrimitive.java +++ b/test/jdk/sun/security/ec/ECDSAPrimitive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -249,7 +249,7 @@ public class ECDSAPrimitive { byte[] u1Bytes = u1.asByteArray(length); byte[] u2Bytes = u2.asByteArray(length); - AffinePoint publicKeyPoint = ECDSAOperations.toAffinePoint(publicKey, + AffinePoint publicKeyPoint = AffinePoint.fromECPoint(publicKey, ecOps.getField()); MutablePoint R = ecOps.multiply(publicKeyPoint, u2Bytes); AffinePoint a1 = ops.basePointMultiply(u1Bytes); diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java new file mode 100644 index 00000000000..8512640bb01 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java @@ -0,0 +1,69 @@ +/* + * 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.net.ssl.X509TrustManager; +import java.io.File; +import java.security.Security; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Date; + +/* + * @test + * @bug 8369282 + * @summary Check that TLS Server certificates chaining back to distrusted + * Chunghwa root are invalid + * @library /test/lib + * @modules java.base/sun.security.validator + * @run main/othervm Chunghwa after policyOn invalid + * @run main/othervm Chunghwa after policyOff valid + * @run main/othervm Chunghwa before policyOn valid + * @run main/othervm Chunghwa before policyOff valid + */ + +public class Chunghwa { + + private static final String CERT_PATH = "chains" + File.separator + "chunghwa"; + + // The ePKI root has a test certificate chain stored in a file + // named "-chain.pem". + private static final String ROOT_TO_TEST = "chunghwaepkirootca"; + + // Date after the restrictions take effect + private static final ZonedDateTime DISTRUST_DATE = + LocalDate.of(2026, 03, 18).atStartOfDay(ZoneOffset.UTC); + + public static void main(String[] args) throws Exception { + + Distrust distrust = new Distrust(args); + + X509TrustManager[] tms = new X509TrustManager[]{ + distrust.getTMF("PKIX", null), + distrust.getTMF("SunX509", null) + }; + + Date notBefore = distrust.getNotBefore(DISTRUST_DATE); + distrust.testCertificateChain(CERT_PATH, notBefore, tms, ROOT_TO_TEST); + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem new file mode 100644 index 00000000000..fdaa6ee04df --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem @@ -0,0 +1,50 @@ +Owner: CN=HiPKI Root CA - G1, + O="Chunghwa Telecom Co., Ltd.", C=TW +Issuer: OU=ePKI Root Certification Authority, + O="Chunghwa Telecom Co., Ltd.", C=TW +Serial number: 23fba648360e15e92ba78aedb67a0ae5 +Valid from: Wed Dec 20 19:11:23 MST 2023 until: Tue Dec 19 08:59:59 MST 2034 +Certificate fingerprints: + SHA1: 87:F1:DD:3B:8E:F1:E0:8C:A8:CA:CB:9B:CE:4E:26:5A:E4:4E:05:F2 + SHA256: 68:07:C9:72:35:C5:EC:60:90:26:9A:4B:5F:ED:FA:B4:69:86:E4:2F:4D:67:D2:ED:DD:CF:6E:45:CF:0D:FA:80 +Signature algorithm name: SHA256withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 + +-----BEGIN CERTIFICATE----- +MIIGjDCCBHSgAwIBAgIQI/umSDYOFekrp4rttnoK5TANBgkqhkiG9w0BAQsFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0yMzEyMjEwMjExMjNaFw0zNDEyMTkxNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQ +S0kgUm9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +9B5/UnMyDHPkvRN0o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh +8Ge6zCFovkRTv4354twvVcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux5 +5199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEu +iAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRt +U6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd +hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXT +T3OUM3ECoWqj1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK +9p/7qxj3ccC2HTHsOyDry+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8 +b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8Pg +cSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NV +vxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaOCAVMwggFPMB8G +A1UdIwQYMBaAFB4M97Zn8uGSJglFwFU5Lnc/QkqiMB0GA1UdDgQWBBTydxf6Xqj+ +9j1x1Wi6yUYMONivsDAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYv +aHR0cDovL2VjYS5oaW5ldC5uZXQvcmVwb3NpdG9yeS9DUkxfU0hBMi9DQS5jcmww +gYIGCCsGAQUFBwEBBHYwdDA7BggrBgEFBQcwAoYvaHR0cDovL2VjYS5oaW5ldC5u +ZXQvcmVwb3NpdG9yeS9DZXJ0cy9lQ0FHMS5jcnQwNQYIKwYBBQUHMAGGKWh0dHA6 +Ly9vY3NwLmVjYS5oaW5ldC5uZXQvT0NTUC9vY3NwRzFzaGEyMBIGA1UdEwEB/wQI +MAYBAf8CAQEwIgYDVR0gBBswGTAIBgZngQwBAgIwDQYLKwYBBAGBtyNkAAMwDQYJ +KoZIhvcNAQELBQADggIBACY9pps8fqk3p8Xqv/qr26I1aFA4jOEG3VWd2bqn68Y9 +InOMZozTMVh7iOnOfat7mEqn/RNhikvR5MOV3qAeg4gwgNb1OMuGltwfXWGiuGeT +vhimsV6E2hhJFAmZyXtfuoV9vSrnr1a5pCWqhVYWSCvoAQ/8Kv0tATKbIe21CYXz +NIo7O9QBSXt0BiaP9+CVQtJAYYuy2MNAcXgzgL4rownrYYAixhPmkxQE0Dt1gVbW +s2htBLJGse0z1fJDblY0Zar4t2ly+kIScx5DhRrrd8XKMK0YvID9Ythb+ao8m7Wd +Kymqr36benGL3GsvmSypLPlqZtfEqVITFhXwQiL8ruxoL+3WfNQJ09x0iV4xaP+E +bZSLLVzIiyhU49YdFHaqKyAJQvzgF2Za3DOwQWlP7OngtUx0ScEGHsoo78AM+Y0T +eLFxmr82kuyH18wZkUT9bLZlot11P2aC8VTprBGr+jEAMJjpmEjSA83ja/ttmqgh +qjj29Jnw3Lgy91XIhzBFMxMYo+hhYeBRmBFWl5+Y5oxBgPVLZpDJvg2rKa8xdqim +KgvF0DMKHntE0hhVy7JfUCnKovNQ0pf0NodLfjpqcCS2GBZ1mNcsW2MG2uBPANcn +LRXmt7N4XX11mctQTADwt8yZZ+2HDrST4kghOz+FXgftrPBdtDtM0T6WJcHWR1uS +-----END CERTIFICATE----- diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java deleted file mode 100644 index 2b075e0f13c..00000000000 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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.test; - -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.UncheckedIOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.Consumer; -import java.util.spi.ToolProvider; -import java.util.stream.Stream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -public class ExecutorTest extends JUnitAdapter { - - private record Command(List stdout, List stderr) { - Command { - stdout.forEach(Objects::requireNonNull); - stderr.forEach(Objects::requireNonNull); - } - - List asExecutable() { - final List commandline = new ArrayList<>(); - if (TKit.isWindows()) { - commandline.addAll(List.of("cmd", "/C")); - } else { - commandline.addAll(List.of("sh", "-c")); - } - commandline.add(Stream.concat(createEchoCommands(stdout), - createEchoCommands(stderr).map(v -> v + ">&2")).collect(joining(" && "))); - return commandline; - } - - private static Stream createEchoCommands(List lines) { - return lines.stream().map(line -> { - if (TKit.isWindows()) { - return "(echo " + line + ")"; - } else { - return "echo " + line; - } - }); - } - - ToolProvider asToolProvider() { - return new ToolProvider() { - - @Override - public int run(PrintWriter out, PrintWriter err, String... args) { - stdout.forEach(out::println); - stderr.forEach(err::println); - return 0; - } - - @Override - public String name() { - return "test"; - } - }; - } - } - - private enum OutputData { - EMPTY(List.of()), - ONE_LINE(List.of("Jupiter")), - MANY(List.of("Uranus", "Saturn", "Earth")); - - OutputData(List data) { - data.forEach(Objects::requireNonNull); - this.data = data; - } - - final List data; - } - - private record CommandSpec(OutputData stdout, OutputData stderr) { - CommandSpec { - Objects.requireNonNull(stdout); - Objects.requireNonNull(stderr); - } - - Command command() { - return new Command(stdout.data.stream().map(line -> { - return "stdout." + line; - }).toList(), stderr.data.stream().map(line -> { - return "stderr." + line; - }).toList()); - } - } - - public enum OutputControl { - DUMP(Executor::dumpOutput), - SAVE_ALL(Executor::saveOutput), - SAVE_FIRST_LINE(Executor::saveFirstLineOfOutput), - DISCARD_STDOUT(Executor::discardStdout), - DISCARD_STDERR(Executor::discardStderr), - ; - - OutputControl(Consumer configureExector) { - this.configureExector = Objects.requireNonNull(configureExector); - } - - Executor applyTo(Executor exec) { - configureExector.accept(exec); - return exec; - } - - static List> variants() { - final List> variants = new ArrayList<>(); - for (final var withDump : BOOLEAN_VALUES) { - variants.addAll(Stream.of( - Set.of(), - Set.of(SAVE_ALL), - Set.of(SAVE_FIRST_LINE), - Set.of(DISCARD_STDOUT), - Set.of(DISCARD_STDERR), - Set.of(SAVE_ALL, DISCARD_STDOUT), - Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT), - Set.of(SAVE_ALL, DISCARD_STDERR), - Set.of(SAVE_FIRST_LINE, DISCARD_STDERR), - Set.of(SAVE_ALL, DISCARD_STDOUT, DISCARD_STDERR), - Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT, DISCARD_STDERR) - ).map(v -> { - if (withDump) { - return Stream.concat(Stream.of(DUMP), v.stream()).collect(toSet()); - } else { - return v; - } - }).toList()); - } - return variants.stream().map(options -> { - return options.stream().filter(o -> { - return o.configureExector != NOP; - }).collect(toSet()); - }).distinct().toList(); - } - - private final Consumer configureExector; - - static final Set SAVE = Set.of(SAVE_ALL, SAVE_FIRST_LINE); - } - - public record OutputTestSpec(boolean toolProvider, Set outputControl, CommandSpec commandSpec) { - public OutputTestSpec { - outputControl.forEach(Objects::requireNonNull); - if (outputControl.containsAll(OutputControl.SAVE)) { - throw new IllegalArgumentException(); - } - Objects.requireNonNull(commandSpec); - } - - @Override - public String toString() { - final List tokens = new ArrayList<>(); - - if (toolProvider) { - tokens.add("tool-provider"); - } - - tokens.add("output=" + format(outputControl)); - tokens.add("command=" + commandSpec); - - return String.join(",", tokens.toArray(String[]::new)); - } - - void test() { - final var command = commandSpec.command(); - final var commandWithDiscardedStreams = discardStreams(command); - - final Executor.Result[] result = new Executor.Result[1]; - final var outputCapture = OutputCapture.captureOutput(() -> { - result[0] = createExecutor(command).executeWithoutExitCodeCheck(); - }); - - assertEquals(0, result[0].getExitCode()); - - // If we dump the subprocesses's output, and the command produced both STDOUT and STDERR, - // then the captured STDOUT may contain interleaved command's STDOUT and STDERR, - // not in sequential order (STDOUT followed by STDERR). - // In this case don't check the contents of the captured command's STDOUT. - if (toolProvider || outputCapture.outLines().isEmpty() || (command.stdout().isEmpty() || command.stderr().isEmpty())) { - assertEquals(expectedCapturedSystemOut(commandWithDiscardedStreams), outputCapture.outLines()); - } - assertEquals(expectedCapturedSystemErr(commandWithDiscardedStreams), outputCapture.errLines()); - - assertEquals(expectedResultStdout(commandWithDiscardedStreams), result[0].stdout().getOutput()); - assertEquals(expectedResultStderr(commandWithDiscardedStreams), result[0].stderr().getOutput()); - - if (!saveOutput()) { - assertNull(result[0].getOutput()); - } else { - assertNotNull(result[0].getOutput()); - final var allExpectedOutput = expectedCommandOutput(command); - assertEquals(allExpectedOutput.isEmpty(), result[0].getOutput().isEmpty()); - if (!allExpectedOutput.isEmpty()) { - if (outputControl.contains(OutputControl.SAVE_ALL)) { - assertEquals(allExpectedOutput, result[0].getOutput()); - } else if (outputControl.contains(OutputControl.SAVE_FIRST_LINE)) { - assertEquals(1, result[0].getOutput().size()); - assertEquals(allExpectedOutput.getFirst(), result[0].getFirstLineOfOutput()); - } else { - throw new UnsupportedOperationException(); - } - } - } - } - - private boolean dumpOutput() { - return outputControl.contains(OutputControl.DUMP); - } - - private boolean saveOutput() { - return !Collections.disjoint(outputControl, OutputControl.SAVE); - } - - private boolean discardStdout() { - return outputControl.contains(OutputControl.DISCARD_STDOUT); - } - - private boolean discardStderr() { - return outputControl.contains(OutputControl.DISCARD_STDERR); - } - - private static String format(Set outputControl) { - return outputControl.stream().map(OutputControl::name).sorted().collect(joining("+")); - } - - private List expectedCapturedSystemOut(Command command) { - if (!dumpOutput() || (!toolProvider && !saveOutput())) { - return List.of(); - } else if(saveOutput()) { - return Stream.concat(command.stdout().stream(), command.stderr().stream()).toList(); - } else { - return command.stdout(); - } - } - - private List expectedCapturedSystemErr(Command command) { - if (!dumpOutput() || (!toolProvider && !saveOutput())) { - return List.of(); - } else if(saveOutput()) { - return List.of(); - } else { - return command.stderr(); - } - } - - private List expectedResultStdout(Command command) { - return expectedResultStream(command.stdout()); - } - - private List expectedResultStderr(Command command) { - if (outputControl.contains(OutputControl.SAVE_FIRST_LINE) && !command.stdout().isEmpty()) { - return List.of(); - } - return expectedResultStream(command.stderr()); - } - - private List expectedResultStream(List commandOutput) { - Objects.requireNonNull(commandOutput); - if (outputControl.contains(OutputControl.SAVE_ALL)) { - return commandOutput; - } else if (outputControl.contains(OutputControl.SAVE_FIRST_LINE)) { - return commandOutput.stream().findFirst().map(List::of).orElseGet(List::of); - } else { - return null; - } - } - - private Command discardStreams(Command command) { - return new Command(discardStdout() ? List.of() : command.stdout(), discardStderr() ? List.of() : command.stderr()); - } - - private record OutputCapture(byte[] out, byte[] err, Charset outCharset, Charset errCharset) { - OutputCapture { - Objects.requireNonNull(out); - Objects.requireNonNull(err); - Objects.requireNonNull(outCharset); - Objects.requireNonNull(errCharset); - } - - List outLines() { - return toLines(out, outCharset); - } - - List errLines() { - return toLines(err, errCharset); - } - - private static List toLines(byte[] buf, Charset charset) { - try (var reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf), charset))) { - return reader.lines().filter(line -> { - return !line.contains("TRACE"); - }).toList(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - static OutputCapture captureOutput(Runnable runnable) { - final var captureOut = new ByteArrayOutputStream(); - final var captureErr = new ByteArrayOutputStream(); - - final var out = System.out; - final var err = System.err; - try { - final var outCharset = System.out.charset(); - final var errCharset = System.err.charset(); - System.setOut(new PrintStream(captureOut, true, outCharset)); - System.setErr(new PrintStream(captureErr, true, errCharset)); - runnable.run(); - return new OutputCapture(captureOut.toByteArray(), captureErr.toByteArray(), outCharset, errCharset); - } finally { - try { - System.setOut(out); - } finally { - System.setErr(err); - } - } - } - } - - private List expectedCommandOutput(Command command) { - command = discardStreams(command); - return Stream.of(command.stdout(), command.stderr()).flatMap(List::stream).toList(); - } - - private Executor createExecutor(Command command) { - final Executor exec; - if (toolProvider) { - exec = Executor.of(command.asToolProvider()); - } else { - exec = Executor.of(command.asExecutable()); - } - - outputControl.forEach(control -> control.applyTo(exec)); - - return exec; - } - } - - @ParameterizedTest - @MethodSource - public void testSavedOutput(OutputTestSpec spec) { - spec.test(); - } - - public static List testSavedOutput() { - List testCases = new ArrayList<>(); - for (final var toolProvider : BOOLEAN_VALUES) { - for (final var outputControl : OutputControl.variants()) { - for (final var stdoutContent : List.of(OutputData.values())) { - for (final var stderrContent : List.of(OutputData.values())) { - final var commandSpec = new CommandSpec(stdoutContent, stderrContent); - testCases.add(new OutputTestSpec(toolProvider, outputControl, commandSpec)); - } - } - } - } - return testCases; - } - - private static final List BOOLEAN_VALUES = List.of(Boolean.TRUE, Boolean.FALSE); - private static final Consumer NOP = exec -> {}; -} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java new file mode 100644 index 00000000000..b0d44702d47 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java @@ -0,0 +1,183 @@ +/* + * 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.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.spi.ToolProvider; +import jdk.jpackage.internal.util.function.ExceptionBox; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class JPackageCommandTest extends JUnitAdapter.TestSrcInitializer { + + @ParameterizedTest + @MethodSource + void testUseToolProvider(UseToolProviderTestSpec spec) { + // Run the test with the new state to avoid UnsupportedOperationException + // that will be thrown if it attempts to alter global variables in the default R/O state. + TKit.withNewState(spec::test); + } + + private static List testUseToolProvider() { + + var testCases = new ArrayList(); + + for (var globalToolProvider : ExecutableSetterType.values()) { + for (var instanceToolProvider : ExecutableSetterType.values()) { + testCases.add(new UseToolProviderTestSpec(globalToolProvider, instanceToolProvider)); + } + } + + return testCases; + } + + record UseToolProviderTestSpec(ExecutableSetterType globalType, ExecutableSetterType instanceType) { + + UseToolProviderTestSpec { + Objects.requireNonNull(globalType); + Objects.requireNonNull(instanceType); + } + + @Override + public String toString() { + return String.format("%s, global=%s", instanceType, globalType); + } + + void test() { + + final Optional global; + switch (globalType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + global = Optional.of(createNewToolProvider("jpackage-mock-global")); + JPackageCommand.useToolProviderByDefault(global.get()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + global = Optional.of(JavaTool.JPACKAGE.asToolProvider()); + JPackageCommand.useToolProviderByDefault(); + } + case SET_PROCESS -> { + global = Optional.empty(); + JPackageCommand.useExecutableByDefault(); + } + case SET_NONE -> { + global = Optional.empty(); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + + var cmd = new JPackageCommand(); + + final Optional instance; + switch (instanceType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + instance = Optional.of(createNewToolProvider("jpackage-mock")); + cmd.useToolProvider(instance.get()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + instance = Optional.of(JavaTool.JPACKAGE.asToolProvider()); + cmd.useToolProvider(true); + } + case SET_PROCESS -> { + instance = Optional.empty(); + cmd.useToolProvider(false); + } + case SET_NONE -> { + instance = Optional.empty(); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + + var actual = cmd.createExecutor().getToolProvider(); + + switch (instanceType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + assertSame(actual.get(), instance.get()); + assertTrue(cmd.isWithToolProvider()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + global.ifPresentOrElse(expected -> { + assertEquals(expected.name(), actual.orElseThrow().name()); + }, () -> { + assertEquals(instance.get().name(), actual.get().name()); + }); + assertTrue(cmd.isWithToolProvider()); + } + case SET_PROCESS -> { + assertFalse(actual.isPresent()); + assertFalse(cmd.isWithToolProvider()); + } + case SET_NONE -> { + switch (globalType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + assertSame(global.get(), actual.get()); + assertTrue(cmd.isWithToolProvider()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + assertEquals(global.get().name(), actual.orElseThrow().name()); + assertTrue(cmd.isWithToolProvider()); + } + case SET_PROCESS, SET_NONE -> { + assertFalse(actual.isPresent()); + assertFalse(cmd.isWithToolProvider()); + } + } + } + } + } + + private static ToolProvider createNewToolProvider(String name) { + return new ToolProvider() { + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public String name() { + return name; + } + }; + } + } + + enum ExecutableSetterType { + SET_DEFAULT_TOOL_PROVIDER, + SET_CUSTOM_TOOL_PROVIDER, + SET_PROCESS, + SET_NONE, + ; + } +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java index 16909d0eb40..3e169b9e184 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.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 @@ -213,7 +213,7 @@ public class PackageTestTest extends JUnitAdapter { @Override public void accept(JPackageCommand cmd, Executor.Result result) { tick(); - jpackageExitCode = result.exitCode(); + jpackageExitCode = result.getExitCode(); } @Override @@ -371,8 +371,7 @@ public class PackageTestTest extends JUnitAdapter { } catch (IOException ex) { throw new UncheckedIOException(ex); } - return new Executor.Result(actualJPackageExitCode, - this::getPrintableCommandLine).assertExitCodeIs(expectedExitCode); + return new Executor.Result(actualJPackageExitCode).assertExitCodeIs(expectedExitCode); } }; }).setExpectedExitCode(expectedJPackageExitCode) diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 94f5b0a52b4..55b13a06620 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.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 @@ -28,7 +28,6 @@ import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; @@ -48,7 +47,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; public record AppImageFile(String mainLauncherName, Optional mainLauncherClassName, - String version, boolean macSigned, boolean macAppStore, Map> launchers) { + String version, boolean macAppStore, Map> launchers) { public static Path getPathInAppImage(Path appImageDir) { return ApplicationLayout.platformAppImage() @@ -67,7 +66,7 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche } public AppImageFile(String mainLauncherName, Optional mainLauncherClassName) { - this(mainLauncherName, mainLauncherClassName, "1.0", false, false, Map.of(mainLauncherName, Map.of())); + this(mainLauncherName, mainLauncherClassName, "1.0", false, Map.of(mainLauncherName, Map.of())); } public AppImageFile(String mainLauncherName, String mainLauncherClassName) { @@ -104,10 +103,6 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche xml.writeEndElement(); })); - xml.writeStartElement("signed"); - xml.writeCharacters(Boolean.toString(macSigned)); - xml.writeEndElement(); - xml.writeStartElement("app-store"); xml.writeCharacters(Boolean.toString(macAppStore)); xml.writeEndElement(); @@ -132,7 +127,7 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche public static AppImageFile load(Path appImageDir) { return toSupplier(() -> { Document doc = XmlUtils.initDocumentBuilder().parse( - Files.newInputStream(getPathInAppImage(appImageDir))); + getPathInAppImage(appImageDir).toFile()); XPath xPath = XPathFactory.newInstance().newXPath(); @@ -141,10 +136,6 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche var mainLauncherClassName = Optional.ofNullable(xPath.evaluate( "/jpackage-state/main-class/text()", doc)); - var macSigned = Optional.ofNullable(xPath.evaluate( - "/jpackage-state/signed/text()", doc)).map( - Boolean::parseBoolean).orElse(false); - var macAppStore = Optional.ofNullable(xPath.evaluate( "/jpackage-state/app-store/text()", doc)).map( Boolean::parseBoolean).orElse(false); @@ -172,7 +163,6 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche mainLauncherName, mainLauncherClassName, version, - macSigned, macAppStore, Collections.unmodifiableMap(launchers)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index ef118e525c5..b508d4f5ffe 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,9 @@ */ package jdk.jpackage.test; -import static java.util.stream.Collectors.joining; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.StringReader; import java.io.UncheckedIOException; -import java.io.Writer; +import java.nio.charset.Charset; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -43,15 +34,18 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.regex.Pattern; import java.util.spi.ToolProvider; -import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; -import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.internal.util.CommandLineFormat; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.RetryExecutor; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingSupplier; public final class Executor extends CommandArguments { @@ -69,8 +63,7 @@ public final class Executor extends CommandArguments { } public Executor() { - outputStreamsControl = new OutputStreamsControl(); - winEnglishOutput = false; + commandOutputControl.dumpStdout(TKit.state().out()).dumpStderr(TKit.state().err()); } public Executor setExecutable(String v) { @@ -93,6 +86,10 @@ public final class Executor extends CommandArguments { return setToolProvider(v.asToolProvider()); } + public Optional getToolProvider() { + return Optional.ofNullable(toolProvider); + } + public Optional getExecutable() { return Optional.ofNullable(executable); } @@ -118,15 +115,6 @@ public final class Executor extends CommandArguments { return this; } - public Executor setWinRunWithEnglishOutput(boolean value) { - if (!TKit.isWindows()) { - throw new UnsupportedOperationException( - "setWinRunWithEnglishOutput is only valid on Windows platform"); - } - winEnglishOutput = value; - return this; - } - public Executor setWindowsTmpDir(String tmp) { if (!TKit.isWindows()) { throw new UnsupportedOperationException( @@ -136,62 +124,31 @@ public final class Executor extends CommandArguments { return this; } - /** - * Configures this instance to save all stdout and stderr streams from the to be - * executed command. - *

    - * This function is mutually exclusive with {@link #saveFirstLineOfOutput()}. - * - * @return this - */ public Executor saveOutput() { return saveOutput(true); } - /** - * Configures if all stdout and stderr streams from the to be executed command - * should be saved. - *

    - * If v is true, the function call is equivalent to - * {@link #saveOutput()} call. If v is false, command - * output will not be saved. - * - * @parameter v if both stdout and stderr streams should be saved - * - * @return this - */ public Executor saveOutput(boolean v) { - return setOutputControl(v, OutputControlOption.SAVE_ALL); + commandOutputControl.saveOutput(v); + return this; } - /** - * Configures this instance to save the first line of a stream merged from - * stdout and stderr streams from the to be executed command. - *

    - * This function is mutually exclusive with {@link #saveOutput()}. - * - * @return this - */ public Executor saveFirstLineOfOutput() { - return setOutputControl(true, OutputControlOption.SAVE_FIRST_LINE); + commandOutputControl.saveFirstLineOfOutput(); + return this; } - /** - * Configures this instance to dump both stdout and stderr streams from the to - * be executed command into {@link System.out}. - * - * @return this - */ public Executor dumpOutput() { return dumpOutput(true); } public Executor dumpOutput(boolean v) { - return setOutputControl(v, OutputControlOption.DUMP); + commandOutputControl.dumpOutput(v); + return this; } public Executor discardStdout(boolean v) { - outputStreamsControl.stdout().discard(v); + commandOutputControl.discardStdout(v); return this; } @@ -200,7 +157,7 @@ public final class Executor extends CommandArguments { } public Executor discardStderr(boolean v) { - outputStreamsControl.stderr().discard(v); + commandOutputControl.discardStderr(v); return this; } @@ -208,45 +165,131 @@ public final class Executor extends CommandArguments { return discardStderr(true); } - public interface Output { - public List getOutput(); - - public default String getFirstLineOfOutput() { - return findFirstLineOfOutput().orElseThrow(); - } - - public default Optional findFirstLineOfOutput() { - return getOutput().stream().findFirst(); - } + public Executor binaryOutput(boolean v) { + commandOutputControl.binaryOutput(v); + return this; } - public record Result(int exitCode, CommandOutput output, Supplier cmdline) implements Output { + public Executor binaryOutput() { + return binaryOutput(true); + } + + public Executor charset(Charset v) { + commandOutputControl.charset(v); + return this; + } + + public Charset charset() { + return commandOutputControl.charset(); + } + + Executor storeOutputInFiles(boolean v) { + commandOutputControl.storeOutputInFiles(v); + return this; + } + + Executor storeOutputInFiles() { + return storeOutputInFiles(true); + } + + public Executor processListener(Consumer v) { + commandOutputControl.processListener(v); + return this; + } + + public record Result(CommandOutputControl.Result base) { public Result { - Objects.requireNonNull(output); - Objects.requireNonNull(cmdline); + Objects.requireNonNull(base); } - public Result(int exitCode, Supplier cmdline) { - this(exitCode, CommandOutput.EMPTY, cmdline); + public Result(int exitCode) { + this(new CommandOutputControl.Result(exitCode)); } - @Override public List getOutput() { - return output.lines().orElse(null); + return base.content(); } - public Output stdout() { - return createView(output.stdoutLines()); + public String getFirstLineOfOutput() { + return getOutput().getFirst(); } - public Output stderr() { - return createView(output.stderrLines()); + public List stdout() { + return base.stdout(); } - public Result assertExitCodeIs(int expectedExitCode) { - TKit.assertEquals(expectedExitCode, exitCode, String.format( - "Check command %s exited with %d code", - cmdline.get(), expectedExitCode)); + public List stderr() { + return base.stderr(); + } + + public Optional> findContent() { + return base.findContent(); + } + + public Optional> findStdout() { + return base.findStdout(); + } + + public Optional> findStderr() { + return base.findStderr(); + } + + public byte[] byteContent() { + return base.byteContent(); + } + + public byte[] byteStdout() { + return base.byteStdout(); + } + + public byte[] byteStderr() { + return base.byteStderr(); + } + + public Optional findByteContent() { + return base.findByteContent(); + } + + public Optional findByteStdout() { + return base.findByteStdout(); + } + + public Optional findByteStderr() { + return base.findByteStderr(); + } + + public Result toCharacterResult(Charset charset, boolean keepByteContent) { + try { + return new Result(base.toCharacterResult(charset, keepByteContent)); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + public Result assertExitCodeIs(int main, int... other) { + if (other.length != 0) { + return assertExitCodeIs(IntStream.concat(IntStream.of(main), IntStream.of(other)).boxed().toList()); + } else { + return assertExitCodeIs(List.of(main)); + } + } + + private Result assertExitCodeIs(List expectedExitCodes) { + Objects.requireNonNull(expectedExitCodes); + switch (expectedExitCodes.size()) { + case 0 -> { + throw new IllegalArgumentException(); + } case 1 -> { + long expectedExitCode = expectedExitCodes.getFirst(); + TKit.assertEquals(expectedExitCode, getExitCode(), String.format( + "Check command %s exited with %d code", + base.execAttrs().printableCommandLine(), expectedExitCode)); + } default -> { + TKit.assertTrue(expectedExitCodes.contains(getExitCode()), String.format( + "Check command %s exited with one of %s codes", + base.execAttrs().printableCommandLine(), expectedExitCodes.stream().sorted().toList())); + } + } return this; } @@ -255,16 +298,11 @@ public final class Executor extends CommandArguments { } public int getExitCode() { - return exitCode; + return base.getExitCode(); } - private static Output createView(Optional> lines) { - return new Output() { - @Override - public List getOutput() { - return lines.orElse(null); - } - }; + public String getPrintableCommandLine() { + return base.execAttrs().printableCommandLine(); } } @@ -274,11 +312,6 @@ public final class Executor extends CommandArguments { "Can't change directory when using tool provider"); } - if (toolProvider != null && winEnglishOutput) { - throw new IllegalArgumentException( - "Can't change locale when using tool provider"); - } - return ThrowingSupplier.toSupplier(() -> { if (toolProvider != null) { return runToolProvider(); @@ -292,8 +325,8 @@ public final class Executor extends CommandArguments { }).get(); } - public Result execute(int expectedCode) { - return executeWithoutExitCodeCheck().assertExitCodeIs(expectedCode); + Result execute(int mainExitCode, int... otherExitCodes) { + return executeWithoutExitCodeCheck().assertExitCodeIs(mainExitCode, otherExitCodes); } public Result execute() { @@ -301,28 +334,36 @@ public final class Executor extends CommandArguments { } public String executeAndGetFirstLineOfOutput() { - return saveFirstLineOfOutput().execute().getFirstLineOfOutput(); + return saveFirstLineOfOutput().execute().getOutput().getFirst(); } public List executeAndGetOutput() { return saveOutput().execute().getOutput(); } - private static class BadResultException extends RuntimeException { - BadResultException(Result v) { - value = v; + private static class FailedAttemptException extends Exception { + FailedAttemptException(Exception cause) { + super(Objects.requireNonNull(cause)); } - Result getValue() { - return value; - } - - private final transient Result value; private static final long serialVersionUID = 1L; } + public RetryExecutor retryUntilExitCodeIs( + int mainExpectedExitCode, int... otherExpectedExitCodes) { + return new RetryExecutor(UnexpectedExitCodeException.class).setExecutable(() -> { + var result = executeWithoutExitCodeCheck(); + result.base().expectExitCode(mainExpectedExitCode, otherExpectedExitCodes); + return result; + }).setExceptionMapper((UnexpectedExitCodeException ex) -> { + createResult(ex.getResult()).assertExitCodeIs(mainExpectedExitCode, otherExpectedExitCodes); + // Unreachable, because the above `Result.assertExitCodeIs(...)` must throw. + throw ExceptionBox.reachedUnreachable(); + }); + } + /** - * Executes the configured command {@code max} at most times and waits for + * Executes the configured command at most {@code max} times and waits for * {@code wait} seconds between each execution until the command exits with * {@code expectedCode} exit code. * @@ -332,17 +373,10 @@ public final class Executor extends CommandArguments { * command */ public Result executeAndRepeatUntilExitCode(int expectedExitCode, int max, int wait) { - try { - return tryRunMultipleTimes(() -> { - Result result = executeWithoutExitCodeCheck(); - if (result.getExitCode() != expectedExitCode) { - throw new BadResultException(result); - } - return result; - }, max, wait).assertExitCodeIs(expectedExitCode); - } catch (BadResultException ex) { - return ex.getValue().assertExitCodeIs(expectedExitCode); - } + return retryUntilExitCodeIs(expectedExitCode) + .setAttemptTimeout(wait, TimeUnit.SECONDS) + .setMaxAttemptsCount(max) + .executeUnchecked(); } /** @@ -359,26 +393,16 @@ public final class Executor extends CommandArguments { * @param wait number of seconds to wait between executions of the */ public static T tryRunMultipleTimes(Supplier task, int max, int wait) { - RuntimeException lastException = null; - int count = 0; - - do { + return new RetryExecutor(FailedAttemptException.class).setExecutable(() -> { try { return task.get(); } catch (RuntimeException ex) { - lastException = ex; + throw new FailedAttemptException(ex); } + }).setExceptionMapper((FailedAttemptException ex) -> { + return (RuntimeException)ex.getCause(); + }).setAttemptTimeout(wait, TimeUnit.SECONDS).setMaxAttemptsCount(max).executeUnchecked(); - try { - Thread.sleep(wait * 1000); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - - count++; - } while (count < max); - - throw lastException; } public static void tryRunMultipleTimes(Runnable task, int max, int wait) { @@ -392,12 +416,6 @@ public final class Executor extends CommandArguments { return saveOutput().executeWithoutExitCodeCheck().getOutput(); } - private Executor setOutputControl(boolean set, OutputControlOption v) { - outputStreamsControl.stdout().set(set, v); - outputStreamsControl.stderr().set(set, v); - return this; - } - private Path executablePath() { if (directory == null || executable.isAbsolute() @@ -413,17 +431,8 @@ public final class Executor extends CommandArguments { return executable.toAbsolutePath(); } - private List prefixCommandLineArgs() { - if (winEnglishOutput) { - return List.of("cmd.exe", "/c", "chcp", "437", ">nul", "2>&1", "&&"); - } else { - return List.of(); - } - } - private Result runExecutable() throws IOException, InterruptedException { List command = new ArrayList<>(); - command.addAll(prefixCommandLineArgs()); command.add(executablePath().toString()); command.addAll(args); ProcessBuilder builder = new ProcessBuilder(command); @@ -431,12 +440,8 @@ public final class Executor extends CommandArguments { builder.environment().put("TMP", winTmpDir); } - outputStreamsControl.applyTo(builder); - StringBuilder sb = new StringBuilder(getPrintableCommandLine()); - outputStreamsControl.describe().ifPresent(desc -> { - sb.append("; ").append(desc); - }); + sb.append("; ").append(commandOutputControl.description()); if (directory != null) { builder.directory(directory.toFile()); @@ -466,141 +471,31 @@ public final class Executor extends CommandArguments { }); } - trace("Execute " + sb.toString() + "..."); - Process process = builder.start(); - - var stdoutGobbler = CompletableFuture.>>supplyAsync(() -> { - try { - return processProcessStream(outputStreamsControl.stdout(), process.getInputStream()); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - }); - - var stderrGobbler = CompletableFuture.>>supplyAsync(() -> { - try { - return processProcessStream(outputStreamsControl.stderr(), process.getErrorStream()); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - }); - - final CommandOutput output; - - try { - output = combine(stdoutGobbler.join(), stderrGobbler.join()); - } catch (CompletionException ex) { - var cause = ex.getCause(); - switch (cause) { - case UncheckedIOException uioex -> { - throw uioex.getCause(); - } - default -> { - throw ExceptionBox.toUnchecked(ExceptionBox.unbox(cause)); - } - } - } - - final int exitCode = process.waitFor(); - trace("Done. Exit code: " + exitCode); - - return createResult(exitCode, output); + return execute(sb, commandOutputControl.createExecutable(builder)); } - private int runToolProvider(PrintStream out, PrintStream err) { + private Result runToolProvider() throws IOException, InterruptedException { final var sb = new StringBuilder(getPrintableCommandLine()); - outputStreamsControl.describe().ifPresent(desc -> { - sb.append("; ").append(desc); - }); - trace("Execute " + sb + "..."); - final int exitCode = toolProvider.run(out, err, args.toArray( - String[]::new)); - trace("Done. Exit code: " + exitCode); - return exitCode; + sb.append("; ").append(commandOutputControl.description()); + + return execute(sb, commandOutputControl.createExecutable(toolProvider, args.toArray(String[]::new))); } - private Result runToolProvider() throws IOException { - final var toolProviderStreamConfig = ToolProviderStreamConfig.create(outputStreamsControl); + private Result execute(StringBuilder traceMsg, CommandOutputControl.Executable exec) throws IOException, InterruptedException { + Objects.requireNonNull(traceMsg); - final var exitCode = runToolProvider(toolProviderStreamConfig); + trace("Execute " + traceMsg + "..."); - final var output = combine( - read(outputStreamsControl.stdout(), toolProviderStreamConfig.out()), - read(outputStreamsControl.stderr(), toolProviderStreamConfig.err())); - return createResult(exitCode, output); + var result = exec.execute(); + + trace("Done. Exit code: " + result.getExitCode()); + + return createResult(result); } - private int runToolProvider(ToolProviderStreamConfig cfg) throws IOException { - try { - return runToolProvider(cfg.out().ps(), cfg.err().ps()); - } finally { - cfg.out().ps().flush(); - cfg.err().ps().flush(); - } - } - - private static Optional> processProcessStream(OutputControl outputControl, InputStream in) throws IOException { - List outputLines = null; - try (final var bufReader = new BufferedReader(new InputStreamReader(in))) { - if (outputControl.dump() || outputControl.saveAll()) { - outputLines = bufReader.lines().toList(); - } else if (outputControl.saveFirstLine()) { - outputLines = Optional.ofNullable(bufReader.readLine()).map(List::of).orElseGet(List::of); - // Read all input, or the started process may exit with an error (cmd.exe does so). - bufReader.transferTo(Writer.nullWriter()); - } else { - // This should be empty input stream, fetch it anyway. - bufReader.transferTo(Writer.nullWriter()); - } - } finally { - if (outputControl.dump() && outputLines != null) { - outputLines.forEach(System.out::println); - if (outputControl.saveFirstLine()) { - outputLines = outputLines.stream().findFirst().map(List::of).orElseGet(List::of); - } - } - if (!outputControl.save()) { - outputLines = null; - } - } - return Optional.ofNullable(outputLines); - } - - private static Optional> read(OutputControl outputControl, CachingPrintStream cps) throws IOException { - final var bufferAsString = cps.bufferContents(); - try (final var bufReader = new BufferedReader(new StringReader(bufferAsString.orElse("")))) { - if (outputControl.saveFirstLine()) { - return Optional.of(bufReader.lines().findFirst().map(List::of).orElseGet(List::of)); - } else if (outputControl.saveAll()) { - return Optional.of(bufReader.lines().toList()); - } else if (bufferAsString.isPresent()) { - return Optional.of(List.of()); - } else { - return Optional.empty(); - } - } - } - - private CommandOutput combine(Optional> out, Optional> err) { - if (out.isEmpty() && err.isEmpty()) { - return new CommandOutput(); - } else if (out.isEmpty()) { - return new CommandOutput(err, -1); - } else if (err.isEmpty()) { - return new CommandOutput(out, Integer.MAX_VALUE); - } else { - final var combined = Stream.of(out, err).map(Optional::orElseThrow).flatMap(List::stream); - if (outputStreamsControl.stdout().saveFirstLine() && outputStreamsControl.stderr().saveFirstLine()) { - return new CommandOutput(Optional.of(combined.findFirst().map(List::of).orElseGet(List::of)), - Integer.min(1, out.orElseThrow().size())); - } else { - return new CommandOutput(Optional.of(combined.toList()), out.orElseThrow().size()); - } - } - } - - private Result createResult(int exitCode, CommandOutput output) { - return new Result(exitCode, output, this::getPrintableCommandLine); + private Result createResult(CommandOutputControl.Result baseResult) { + return new Result(baseResult.copyWithExecutableAttributes( + new ExecutableAttributes(baseResult.execAttrs(), getPrintableCommandLine()))); } public String getPrintableCommandLine() { @@ -615,362 +510,36 @@ public final class Executor extends CommandArguments { exec = executablePath().toString(); } - var cmdline = Stream.of(prefixCommandLineArgs(), List.of(exec), args).flatMap( - List::stream).toList(); + var cmdline = Stream.of(List.of(exec), args).flatMap(List::stream).toList(); - return String.format(format, printCommandLine(cmdline), cmdline.size()); + return String.format(format, CommandLineFormat.DEFAULT.apply(cmdline), cmdline.size()); } - private static String printCommandLine(List cmdline) { - // Want command line printed in a way it can be easily copy/pasted - // to be executed manually - Pattern regex = Pattern.compile("\\s"); - return cmdline.stream().map( - v -> (v.isEmpty() || regex.matcher(v).find()) ? "\"" + v + "\"" : v).collect( - Collectors.joining(" ")); + private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String printableCommandLine) + implements CommandOutputControl.ExecutableAttributes { + + ExecutableAttributes { + Objects.requireNonNull(base); + if (printableCommandLine.isBlank()) { + throw new IllegalArgumentException(); + } + } + + @Override + public List commandLine() { + return base.commandLine(); + } } private static void trace(String msg) { TKit.trace(String.format("exec: %s", msg)); } - private static PrintStream nullPrintStream() { - return new PrintStream(OutputStream.nullOutputStream()); - } - - private record OutputStreamsControl(OutputControl stdout, OutputControl stderr) { - OutputStreamsControl { - Objects.requireNonNull(stdout); - Objects.requireNonNull(stderr); - } - - OutputStreamsControl() { - this(new OutputControl(), new OutputControl()); - } - - void applyTo(ProcessBuilder pb) { - pb.redirectOutput(stdout.asProcessBuilderRedirect()); - pb.redirectError(stderr.asProcessBuilderRedirect()); - } - - Optional describe() { - final List tokens = new ArrayList<>(); - if (stdout.save() || stderr.save()) { - streamsLabel("save ", true).ifPresent(tokens::add); - } - if (stdout.dump() || stderr.dump()) { - streamsLabel("inherit ", true).ifPresent(tokens::add); - } - streamsLabel("discard ", false).ifPresent(tokens::add); - if (tokens.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(String.join("; ", tokens)); - } - } - - Optional streamsLabel(String prefix, boolean negate) { - Objects.requireNonNull(prefix); - final var str = Stream.of(stdoutLabel(negate), stderrLabel(negate)) - .filter(Optional::isPresent) - .map(Optional::orElseThrow) - .collect(joining("+")); - if (str.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(prefix + str); - } - } - - private Optional stdoutLabel(boolean negate) { - if ((stdout.discard() && !negate) || (!stdout.discard() && negate)) { - return Optional.of("out"); - } else { - return Optional.empty(); - } - } - - private Optional stderrLabel(boolean negate) { - if ((stderr.discard() && !negate) || (!stderr.discard() && negate)) { - return Optional.of("err"); - } else { - return Optional.empty(); - } - } - } - - private record CachingPrintStream(PrintStream ps, Optional buf) { - CachingPrintStream { - Objects.requireNonNull(ps); - Objects.requireNonNull(buf); - } - - Optional bufferContents() { - return buf.map(ByteArrayOutputStream::toString); - } - - static Builder build() { - return new Builder(); - } - - static final class Builder { - - Builder save(boolean v) { - save = v; - return this; - } - - Builder discard(boolean v) { - discard = v; - return this; - } - - Builder dumpStream(PrintStream v) { - dumpStream = v; - return this; - } - - CachingPrintStream create() { - final Optional buf; - if (save && !discard) { - buf = Optional.of(new ByteArrayOutputStream()); - } else { - buf = Optional.empty(); - } - - final PrintStream ps; - if (buf.isPresent() && dumpStream != null) { - ps = new PrintStream(new TeeOutputStream(List.of(buf.orElseThrow(), dumpStream)), true, dumpStream.charset()); - } else if (!discard) { - ps = buf.map(PrintStream::new).or(() -> Optional.ofNullable(dumpStream)).orElseGet(Executor::nullPrintStream); - } else { - ps = nullPrintStream(); - } - - return new CachingPrintStream(ps, buf); - } - - private boolean save; - private boolean discard; - private PrintStream dumpStream; - } - } - - private record ToolProviderStreamConfig(CachingPrintStream out, CachingPrintStream err) { - ToolProviderStreamConfig { - Objects.requireNonNull(out); - Objects.requireNonNull(err); - } - - static ToolProviderStreamConfig create(OutputStreamsControl cfg) { - final var errCfgBuilder = cfg.stderr().buildCachingPrintStream(System.err); - if (cfg.stderr().dump() && cfg.stderr().save()) { - errCfgBuilder.dumpStream(System.out); - } - return new ToolProviderStreamConfig( - cfg.stdout().buildCachingPrintStream(System.out).create(), errCfgBuilder.create()); - } - } - - private static final class OutputControl { - - boolean save() { - return save.isPresent(); - } - - boolean saveAll() { - return save.orElse(null) == OutputControlOption.SAVE_ALL; - } - - boolean saveFirstLine() { - return save.orElse(null) == OutputControlOption.SAVE_FIRST_LINE; - } - - boolean discard() { - return discard || (!dump && save.isEmpty()); - } - - boolean dump() { - return !discard && dump; - } - - OutputControl dump(boolean v) { - this.dump = v; - return this; - } - - OutputControl discard(boolean v) { - this.discard = v; - return this; - } - - OutputControl saveAll(boolean v) { - if (v) { - save = Optional.of(OutputControlOption.SAVE_ALL); - } else { - save = Optional.empty(); - } - return this; - } - - OutputControl saveFirstLine(boolean v) { - if (v) { - save = Optional.of(OutputControlOption.SAVE_FIRST_LINE); - } else { - save = Optional.empty(); - } - return this; - } - - OutputControl set(boolean set, OutputControlOption v) { - switch (v) { - case DUMP -> dump(set); - case SAVE_ALL -> saveAll(set); - case SAVE_FIRST_LINE -> saveFirstLine(set); - } - return this; - } - - ProcessBuilder.Redirect asProcessBuilderRedirect() { - if (discard()) { - return ProcessBuilder.Redirect.DISCARD; - } else if (dump && !save()) { - return ProcessBuilder.Redirect.INHERIT; - } else { - return ProcessBuilder.Redirect.PIPE; - } - } - - CachingPrintStream.Builder buildCachingPrintStream(PrintStream dumpStream) { - Objects.requireNonNull(dumpStream); - final var builder = CachingPrintStream.build().save(save()).discard(discard()); - if (dump()) { - builder.dumpStream(dumpStream); - } - return builder; - } - - private boolean dump; - private boolean discard; - private Optional save = Optional.empty(); - } - - private static final class TeeOutputStream extends OutputStream { - - public TeeOutputStream(Iterable streams) { - streams.forEach(Objects::requireNonNull); - this.streams = streams; - } - - @Override - public void write(int b) throws IOException { - for (final var out : streams) { - out.write(b); - } - } - - @Override - public void write(byte[] b) throws IOException { - for (final var out : streams) { - out.write(b); - } - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - for (final var out : streams) { - out.write(b, off, len); - } - } - - @Override - public void flush() throws IOException { - forEach(OutputStream::flush); - } - - @Override - public void close() throws IOException { - forEach(OutputStream::close); - } - - private void forEach(OutputStreamConsumer c) throws IOException { - IOException firstEx = null; - for (final var out : streams) { - try { - c.accept(out); - } catch (IOException e) { - if (firstEx == null) { - firstEx = e; - } - } - } - if (firstEx != null) { - throw firstEx; - } - } - - @FunctionalInterface - private static interface OutputStreamConsumer { - void accept(OutputStream out) throws IOException; - } - - private final Iterable streams; - } - - private static final class CommandOutput { - CommandOutput(Optional> lines, int stdoutLineCount) { - this.lines = Objects.requireNonNull(lines); - this.stdoutLineCount = stdoutLineCount; - } - - CommandOutput() { - this(Optional.empty(), 0); - } - - Optional> lines() { - return lines; - } - - Optional> stdoutLines() { - if (lines.isEmpty() || stdoutLineCount < 0) { - return Optional.empty(); - } - - final var theLines = lines.orElseThrow(); - if (stdoutLineCount == theLines.size()) { - return lines; - } else { - return Optional.of(theLines.subList(0, Integer.min(stdoutLineCount, theLines.size()))); - } - } - - Optional> stderrLines() { - if (lines.isEmpty() || stdoutLineCount > lines.orElseThrow().size()) { - return Optional.empty(); - } else if (stdoutLineCount == 0) { - return lines; - } else { - final var theLines = lines.orElseThrow(); - return Optional.of(theLines.subList(stdoutLineCount, theLines.size())); - } - } - - private final Optional> lines; - private final int stdoutLineCount; - - static final CommandOutput EMPTY = new CommandOutput(); - } - private ToolProvider toolProvider; private Path executable; - private OutputStreamsControl outputStreamsControl; + private final CommandOutputControl commandOutputControl = new CommandOutputControl(); private Path directory; private Set removeEnvVars = new HashSet<>(); private Map setEnvVars = new HashMap<>(); - private boolean winEnglishOutput; private String winTmpDir = null; - - private static enum OutputControlOption { - SAVE_ALL, SAVE_FIRST_LINE, DUMP - } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 6c7b6a25255..bc731e18136 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -315,37 +316,33 @@ public final class HelloApp { public static void executeLauncherAndVerifyOutput(JPackageCommand cmd, String... args) { - AppOutputVerifier av = assertMainLauncher(cmd, args); - if (av != null) { + assertMainLauncher(cmd, args).ifPresent(av -> { av.executeAndVerifyOutput(args); - } + }); } public static Executor.Result executeLauncher(JPackageCommand cmd, String... args) { - AppOutputVerifier av = assertMainLauncher(cmd, args); - if (av != null) { + return assertMainLauncher(cmd, args).map(av -> { return av.saveOutput(true).execute(args); - } else { - return null; - } + }).orElseThrow(); } - public static AppOutputVerifier assertMainLauncher(JPackageCommand cmd, + public static Optional assertMainLauncher(JPackageCommand cmd, String... args) { final Path launcherPath = cmd.appLauncherPath(); if (!cmd.canRunLauncher(String.format("Not running [%s] launcher", launcherPath))) { - return null; + return Optional.empty(); } - return assertApp(launcherPath) + return Optional.of(assertApp(launcherPath) .addDefaultArguments(Optional .ofNullable(cmd.getAllArgumentValues("--arguments")) .orElseGet(() -> new String[0])) .addJavaOptions(Optional .ofNullable(cmd.getAllArgumentValues("--java-options")) - .orElseGet(() -> new String[0])); + .orElseGet(() -> new String[0]))); } @@ -426,6 +423,11 @@ public final class HelloApp { .collect(Collectors.toList())); } + public AppOutputVerifier processListener(Consumer v) { + processListener = v; + return this; + } + public void verifyOutput(String... args) { final List launcherArgs = List.of(args); final List appArgs; @@ -479,6 +481,7 @@ public final class HelloApp { .saveOutput(saveOutput) .dumpOutput() .setExecutable(executablePath) + .processListener(processListener) .addArguments(List.of(args)); env.forEach((envVarName, envVarValue) -> { @@ -493,6 +496,7 @@ public final class HelloApp { private final Path launcherPath; private Path outputFilePath; private int expectedExitCode; + private Consumer processListener; private final List defaultLauncherArgs; private final Map params; private final Map env; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 38091cb3452..9b8b05af93b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,6 +61,7 @@ import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingFunction; import jdk.jpackage.internal.util.function.ThrowingRunnable; @@ -75,20 +76,23 @@ public class JPackageCommand extends CommandArguments { @SuppressWarnings("this-escape") public JPackageCommand() { + toolProviderSource = new ToolProviderSource(); prerequisiteActions = new Actions(); verifyActions = new Actions(); excludeStandardAsserts(StandardAssert.MAIN_LAUNCHER_DESCRIPTION); + removeOldOutputBundle = true; } private JPackageCommand(JPackageCommand cmd, boolean immutable) { args.addAll(cmd.args); - withToolProvider = cmd.withToolProvider; + toolProviderSource = cmd.toolProviderSource.copy(); saveConsoleOutput = cmd.saveConsoleOutput; discardStdout = cmd.discardStdout; discardStderr = cmd.discardStderr; suppressOutput = cmd.suppressOutput; ignoreDefaultRuntime = cmd.ignoreDefaultRuntime; ignoreDefaultVerbose = cmd.ignoreDefaultVerbose; + removeOldOutputBundle = cmd.removeOldOutputBundle; this.immutable = immutable; prerequisiteActions = new Actions(cmd.prerequisiteActions); verifyActions = new Actions(cmd.verifyActions); @@ -768,7 +772,7 @@ public class JPackageCommand extends CommandArguments { } public static void useToolProviderByDefault(ToolProvider jpackageToolProvider) { - defaultToolProvider.set(Optional.of(jpackageToolProvider)); + TKit.state().setProperty(DefaultToolProviderKey.VALUE, Objects.requireNonNull(jpackageToolProvider)); } public static void useToolProviderByDefault() { @@ -776,36 +780,22 @@ public class JPackageCommand extends CommandArguments { } public static void useExecutableByDefault() { - defaultToolProvider.set(Optional.empty()); - } - - /** - * Starts a new thread. In this thread calls - * {@link #useToolProviderByDefault(ToolProvider)} with the specified - * {@code jpackageToolProvider} and then calls {@code workload.run()}. Joins the - * thread. - *

    - * The idea is to run the {@code workload} in the context of the specified - * jpackage {@code ToolProvider} without altering the global variable holding - * the default jpackage {@code ToolProvider}. The global variable is - * thread-local; setting its value in a new thread doesn't alter its copy in the - * calling thread. - * - * @param jpackageToolProvider jpackage {@code ToolProvider} - * @param workload the workload to run - */ - public static void withToolProvider(ToolProvider jpackageToolProvider, Runnable workload) { - Objects.requireNonNull(jpackageToolProvider); - Objects.requireNonNull(workload); - ThrowingRunnable.toRunnable(Thread.ofVirtual().start(() -> { - useToolProviderByDefault(jpackageToolProvider); - workload.run(); - })::join).run(); + TKit.state().setProperty(DefaultToolProviderKey.VALUE, null); } public JPackageCommand useToolProvider(boolean v) { verifyMutable(); - withToolProvider = v; + if (v) { + toolProviderSource.useDefaultToolProvider(); + } else { + toolProviderSource.useProcess(); + } + return this; + } + + public JPackageCommand useToolProvider(ToolProvider v) { + verifyMutable(); + toolProviderSource.useToolProvider(v); return this; } @@ -856,6 +846,28 @@ public class JPackageCommand extends CommandArguments { return this; } + /** + * Configures this instance to optionally remove the existing output bundle + * before running the jpackage command. + * + * @param v {@code true} to remove existing output bundle before running the + * jpackage command, and {@code false} otherwise + * @return this + */ + public JPackageCommand removeOldOutputBundle(boolean v) { + verifyMutable(); + removeOldOutputBundle = v; + return this; + } + + /** + * Returns {@code true} if this instance will remove existing output bundle + * before running the jpackage command, and {@code false} otherwise. + */ + public boolean isRemoveOldOutputBundle() { + return removeOldOutputBundle; + } + public JPackageCommand validateOutput(TKit.TextStreamVerifier validator) { return validateOutput(validator::apply); } @@ -917,9 +929,7 @@ public class JPackageCommand extends CommandArguments { } public boolean isWithToolProvider() { - return Optional.ofNullable(withToolProvider).orElseGet(() -> { - return defaultToolProvider.get().isPresent(); - }); + return toolProviderSource.toolProvider().isPresent(); } public JPackageCommand executePrerequisiteActions() { @@ -927,21 +937,19 @@ public class JPackageCommand extends CommandArguments { return this; } - private Executor createExecutor() { + Executor createExecutor() { Executor exec = new Executor() .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput) .discardStdout(discardStdout).discardStderr(discardStderr) .setDirectory(executeInDirectory) .addArguments(args); - if (isWithToolProvider()) { - exec.setToolProvider(defaultToolProvider.get().orElseGet(JavaTool.JPACKAGE::asToolProvider)); - } else { - exec.setExecutable(JavaTool.JPACKAGE); - if (TKit.isWindows()) { - exec.setWindowsTmpDir(System.getProperty("java.io.tmpdir")); - } - } + toolProviderSource.toolProvider().ifPresentOrElse(exec::setToolProvider, () -> { + exec.setExecutable(JavaTool.JPACKAGE); + if (TKit.isWindows()) { + exec.setWindowsTmpDir(System.getProperty("java.io.tmpdir")); + } + }); return exec; } @@ -962,21 +970,18 @@ public class JPackageCommand extends CommandArguments { verifyMutable(); executePrerequisiteActions(); - if (hasArgument("--dest")) { - nullableOutputBundle().ifPresent(path -> { - ThrowingRunnable.toRunnable(() -> { - if (Files.isDirectory(path)) { - TKit.deleteDirectoryRecursive(path, String.format( - "Delete [%s] folder before running jpackage", - path)); - } else if (TKit.deleteIfExists(path)) { - TKit.trace(String.format( - "Deleted [%s] file before running jpackage", - path)); - } - }).run(); - }); - } + nullableOutputBundle().filter(_ -> { + return removeOldOutputBundle; + }).ifPresent(path -> { + ThrowingRunnable.toRunnable(() -> { + if (Files.isDirectory(path)) { + TKit.deleteDirectoryRecursive(path, + String.format("Delete [%s] folder before running jpackage", path)); + } else if (TKit.deleteIfExists(path)) { + TKit.trace(String.format("Deleted [%s] file before running jpackage", path)); + } + }).run(); + }); Path resourceDir = getArgumentValue("--resource-dir", () -> null, Path::of); if (resourceDir != null && Files.isDirectory(resourceDir)) { @@ -1022,7 +1027,7 @@ public class JPackageCommand extends CommandArguments { outputValidator.accept(result.getOutput().iterator()); } - if (result.exitCode() == 0 && expectedExitCode.isPresent()) { + if (result.getExitCode() == 0 && expectedExitCode.isPresent()) { verifyActions.run(); } @@ -1106,7 +1111,7 @@ public class JPackageCommand extends CommandArguments { private final Map> snapshots; } - public static enum ReadOnlyPathAssert{ + public static enum ReadOnlyPathAssert { APP_IMAGE(new Builder("--app-image").enable(cmd -> { // External app image should be R/O unless it is an app image signing on macOS. return !(TKit.isOSX() && MacHelper.signPredefinedAppImage(cmd)); @@ -1374,7 +1379,7 @@ public class JPackageCommand extends CommandArguments { if (!isImagePackageType() && hasArgument("--app-image")) { // Build native macOS package from an external app image. // If the external app image is signed, ".jpackage.xml" file should be kept, otherwise removed. - return AppImageFile.load(Path.of(getArgumentValue("--app-image"))).macSigned(); + return MacHelper.isBundleSigned(Path.of(getArgumentValue("--app-image"))); } } @@ -1395,13 +1400,8 @@ public class JPackageCommand extends CommandArguments { final AppImageFile aif = AppImageFile.load(rootDir); if (TKit.isOSX()) { - boolean expectedValue = MacHelper.appImageSigned(this); - boolean actualValue = aif.macSigned(); - TKit.assertEquals(expectedValue, actualValue, - "Check for unexpected value of property in app image file"); - - expectedValue = hasArgument("--mac-app-store"); - actualValue = aif.macAppStore(); + var expectedValue = hasArgument("--mac-app-store"); + var actualValue = aif.macAppStore(); TKit.assertEquals(expectedValue, actualValue, "Check for unexpected value of property in app image file"); } @@ -1426,7 +1426,7 @@ public class JPackageCommand extends CommandArguments { } else { if (TKit.isOSX() && hasArgument("--app-image")) { String appImage = getArgumentValue("--app-image"); - if (AppImageFile.load(Path.of(appImage)).macSigned()) { + if (MacHelper.isBundleSigned(Path.of(appImage))) { assertFileNotInAppImage(lookupPath); } else { assertFileInAppImage(lookupPath); @@ -1725,13 +1725,77 @@ public class JPackageCommand extends CommandArguments { private final List actions; } - private Boolean withToolProvider; + private static final class ToolProviderSource { + + ToolProviderSource copy() { + return new ToolProviderSource(this); + } + + void useDefaultToolProvider() { + customToolProvider = null; + mode = Mode.USE_TOOL_PROVIDER; + } + + void useToolProvider(ToolProvider tp) { + customToolProvider = Objects.requireNonNull(tp); + mode = Mode.USE_TOOL_PROVIDER; + } + + void useProcess() { + customToolProvider = null; + mode = Mode.USE_PROCESS; + } + + Optional toolProvider() { + switch (mode) { + case USE_PROCESS -> { + return Optional.empty(); + } + case USE_TOOL_PROVIDER -> { + if (customToolProvider != null) { + return Optional.of(customToolProvider); + } else { + return TKit.state().findProperty(DefaultToolProviderKey.VALUE).map(ToolProvider.class::cast).or(() -> { + return Optional.of(JavaTool.JPACKAGE.asToolProvider()); + }); + } + } + case INHERIT_DEFAULTS -> { + return TKit.state().findProperty(DefaultToolProviderKey.VALUE).map(ToolProvider.class::cast); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + } + + ToolProviderSource() { + mode = Mode.INHERIT_DEFAULTS; + } + + private ToolProviderSource(ToolProviderSource other) { + this.customToolProvider = other.customToolProvider; + this.mode = other.mode; + } + + private enum Mode { + INHERIT_DEFAULTS, + USE_PROCESS, + USE_TOOL_PROVIDER + } + + private ToolProvider customToolProvider; + private Mode mode; + } + + private final ToolProviderSource toolProviderSource; private boolean saveConsoleOutput; private boolean discardStdout; private boolean discardStderr; private boolean suppressOutput; private boolean ignoreDefaultRuntime; private boolean ignoreDefaultVerbose; + private boolean removeOldOutputBundle; private boolean immutable; private final Actions prerequisiteActions; private final Actions verifyActions; @@ -1741,12 +1805,10 @@ public class JPackageCommand extends CommandArguments { private Set readOnlyPathAsserts = Set.of(ReadOnlyPathAssert.values()); private Set standardAsserts = Set.of(StandardAssert.values()); private List>> outputValidators = new ArrayList<>(); - private static InheritableThreadLocal> defaultToolProvider = new InheritableThreadLocal<>() { - @Override - protected Optional initialValue() { - return Optional.empty(); - } - }; + + private enum DefaultToolProviderKey { + VALUE + } private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).collect(toMap(PackageType::getType, x -> x)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java index 15d96311d98..f9fcfb905af 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.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 @@ -39,7 +39,6 @@ import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; -import javax.xml.parsers.ParserConfigurationException; import jdk.jpackage.internal.resources.ResourceLocator; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.function.ThrowingBiConsumer; @@ -367,7 +366,7 @@ public final class LauncherVerifier { } } - private void verifyMacEntitlements(JPackageCommand cmd) throws ParserConfigurationException, SAXException, IOException { + private void verifyMacEntitlements(JPackageCommand cmd) throws SAXException, IOException { Path launcherPath = cmd.appLauncherPath(name); var entitlements = MacSignVerify.findEntitlements(launcherPath); @@ -457,8 +456,10 @@ public final class LauncherVerifier { private static final class DefaultEntitlements { private static Map loadFromResources(String resourceName) { return ThrowingSupplier.toSupplier(() -> { - var bytes = ResourceLocator.class.getResourceAsStream(resourceName).readAllBytes(); - return new PListReader(bytes).toMap(true); + try (var in = ResourceLocator.class.getResourceAsStream(resourceName)) { + var bytes = in.readAllBytes(); + return new PListReader(bytes).toMap(true); + } }).get(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index 3b02a3f6a69..78562b2ed26 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -723,7 +723,9 @@ public final class LinuxHelper { private static Optional queryMimeTypeDefaultHandler(String mimeType) { return Executor.of("xdg-mime", "query", "default", mimeType) - .discardStderr().saveFirstLineOfOutput().execute().findFirstLineOfOutput(); + .discardStderr() + .saveFirstLineOfOutput() + .execute().getOutput().stream().findFirst(); } private static void verifyIconInScriptlet(Scriptlet scriptletType, diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index 92d9fa0bd44..1cb5532d46a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import static jdk.jpackage.internal.util.PListWriter.writeStringArray; import static jdk.jpackage.internal.util.PListWriter.writeStringOptional; import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder; import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; import java.io.ByteArrayInputStream; @@ -45,31 +46,31 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.UnaryOperator; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; -import jdk.jpackage.internal.RetryExecutor; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.PathUtils; +import jdk.jpackage.internal.util.RetryExecutor; import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; @@ -89,39 +90,55 @@ public final class MacHelper { // See JDK-8373105. "hdiutil" does not handle such cases very good. final var mountRoot = TKit.createTempDirectory("mountRoot"); - // Explode DMG assuming this can require interaction, thus use `yes`. - String attachCMD[] = { - "sh", "-c", - String.join(" ", "yes", "|", "/usr/bin/hdiutil", "attach", - JPackageCommand.escapeAndJoin(cmd.outputBundle().toString()), - "-mountroot", PathUtils.normalizedAbsolutePathString(mountRoot), - "-nobrowse", "-plist")}; - RetryExecutor attachExecutor = new RetryExecutor(); - try { - // 10 times with 6 second delays. - attachExecutor.setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(6000) - .setWriteOutputToFile(true) - .saveOutput(true) - .execute(attachCMD); - } catch (IOException ex) { - throw new RuntimeException(ex); - } + // Explode the DMG assuming this can require interaction if the DMG has a license, thus use `yes`. + final var attachExec = Executor.of("sh", "-c", String.join(" ", + "yes", + "|", + "/usr/bin/hdiutil", + "attach", + JPackageCommand.escapeAndJoin(cmd.outputBundle().toString()), + "-mountroot", PathUtils.normalizedAbsolutePathString(mountRoot), + "-nobrowse", + "-plist" + )).saveOutput().storeOutputInFiles().binaryOutput(); - Path mountPoint = null; + final var attachResult = attachExec.executeAndRepeatUntilExitCode(0, 10, 6); + + final Path mountPoint; + + boolean mountPointInitialized = false; try { + byte[] stdout = attachResult.byteStdout(); + + // If the DMG has a license, it will be printed to the stdout before the plist content. + // All bytes before the XML declaration of the plist must be skipped. + // We need to find the location of the {'<', '?', 'x', 'm', 'l'} byte array + // (the XML declaration) in the captured binary stdout. + // Instead of crafting an ad-hoc function that operates on byte arrays, + // we will convert the byte array into a String instance using + // an 8-bit character set (ISO-8859-1) and use the standard String#indexOf(). + var startPlistIndex = new String(stdout, StandardCharsets.ISO_8859_1).indexOf(" 0) { + plistXml = Arrays.copyOfRange(stdout, startPlistIndex, stdout.length); + } else { + plistXml = stdout; + } + // One of "dict" items of "system-entities" array property should contain "mount-point" string property. - mountPoint = readPList(attachExecutor.getOutput()).queryArrayValue("system-entities", false).map(PListReader.class::cast).map(dict -> { - try { - return dict.queryValue("mount-point"); - } catch (NoSuchElementException ex) { - return (String)null; - } - }).filter(Objects::nonNull).map(Path::of).findFirst().orElseThrow(); + mountPoint = readPList(plistXml).queryArrayValue("system-entities", false) + .map(PListReader.class::cast) + .map(dict -> { + return dict.findValue("mount-point"); + }) + .filter(Optional::isPresent).map(Optional::get) + .map(Path::of).findFirst().orElseThrow(); + mountPointInitialized = true; } finally { - if (mountPoint == null) { + if (!mountPointInitialized) { TKit.trace("Unexpected plist file missing `system-entities` array:"); - attachExecutor.getOutput().forEach(TKit::trace); + attachResult.toCharacterResult(attachExec.charset(), false).stdout().forEach(TKit::trace); TKit.trace("Done"); } } @@ -138,39 +155,27 @@ public final class MacHelper { ThrowingConsumer.toConsumer(consumer).accept(childPath); } } finally { - String detachCMD[] = { - "/usr/bin/hdiutil", - "detach", - "-verbose", - mountPoint.toAbsolutePath().toString()}; // "hdiutil detach" might not work right away due to resource busy error, so // repeat detach several times. - RetryExecutor detachExecutor = new RetryExecutor(); - // Image can get detach even if we got resource busy error, so stop - // trying to detach it if it is no longer attached. - final Path mp = mountPoint; - detachExecutor.setExecutorInitializer(exec -> { - if (!Files.exists(mp)) { - detachExecutor.abort(); + new RetryExecutor(RuntimeException.class).setExecutable(context -> { + var exec = Executor.of("/usr/bin/hdiutil", "detach").storeOutputInFiles(); + if (context.isLastAttempt()) { + // The last attempt, force detach. + exec.addArgument("-force"); } - }); - try { - // 10 times with 6 second delays. - detachExecutor.setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(6000) - .setWriteOutputToFile(true) - .saveOutput(true) - .execute(detachCMD); - } catch (IOException ex) { - if (!detachExecutor.isAborted()) { - // Now force to detach if it still attached - if (Files.exists(mountPoint)) { - Executor.of("/usr/bin/hdiutil", "detach", - "-force", "-verbose") - .addArgument(mountPoint).execute(); - } + exec.addArgument(mountPoint); + + // The image can get detached even if we get a resource busy error, + // so execute the detach command without checking the exit code. + var result = exec.executeWithoutExitCodeCheck(); + + if (result.getExitCode() == 0 || !Files.exists(mountPoint)) { + // Detached successfully! + return null; + } else { + throw new RuntimeException(String.format("[%s] mount point still attached", mountPoint)); } - } + }).setMaxAttemptsCount(10).setAttemptTimeout(6, TimeUnit.SECONDS).execute(); } } @@ -184,19 +189,13 @@ public final class MacHelper { public static PListReader readPList(Path path) { TKit.assertReadableFileExists(path); - return ThrowingSupplier.toSupplier(() -> readPList(Files.readAllLines( - path))).get(); + return readPList(toFunction(Files::readAllBytes).apply(path)); } - public static PListReader readPList(List lines) { - return readPList(lines.stream()); - } - - public static PListReader readPList(Stream lines) { - return ThrowingSupplier.toSupplier(() -> new PListReader(lines - // Skip leading lines before xml declaration - .dropWhile(Pattern.compile("\\s?<\\?xml\\b.+\\?>").asPredicate().negate()) - .collect(Collectors.joining()).getBytes(StandardCharsets.UTF_8))).get(); + public static PListReader readPList(byte[] xml) { + return ThrowingSupplier.toSupplier(() -> { + return new PListReader(xml); + }).get(); } public static Map flatMapPList(PListReader plistReader) { @@ -281,13 +280,13 @@ public final class MacHelper { throw new UnsupportedOperationException(); } - var runtimeImage = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of); + var runtimeImageBundle = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of).flatMap(MacBundle::fromPath); var appImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of); - if (cmd.isRuntime() && Files.isDirectory(runtimeImage.orElseThrow().resolve("Contents/_CodeSignature"))) { + if (cmd.isRuntime() && runtimeImageBundle.map(MacHelper::isBundleSigned).orElse(false)) { // If the predefined runtime is a signed bundle, bundled image should be signed too. return true; - } else if (appImage.map(AppImageFile::load).map(AppImageFile::macSigned).orElse(false)) { + } else if (appImage.map(MacHelper::isBundleSigned).orElse(false)) { // The external app image is signed, so the app image is signed too. return true; } @@ -317,6 +316,14 @@ public final class MacHelper { }).run(); } + static boolean isBundleSigned(Path bundleRoot) { + return isBundleSigned(MacBundle.fromPath(bundleRoot).orElseThrow(IllegalArgumentException::new)); + } + + static boolean isBundleSigned(MacBundle bundle) { + return MacSignVerify.findSpctlSignOrigin(MacSignVerify.SpctlType.EXEC, bundle.root(), true).isPresent(); + } + private static void createFaPListFragmentFromFaProperties(JPackageCommand cmd, XMLStreamWriter xml) throws XMLStreamException, IOException { @@ -399,7 +406,7 @@ public final class MacHelper { var predefinedAppImage = Path.of(Optional.ofNullable(cmd.getArgumentValue("--app-image")).orElseThrow(IllegalArgumentException::new)); - var plistPath = ApplicationLayout.macAppImage().resolveAt(predefinedAppImage).contentDirectory().resolve("Info.plist"); + var plistPath = MacBundle.fromPath(predefinedAppImage).orElseThrow().infoPlistFile(); try (var plistStream = Files.newInputStream(plistPath)) { var plist = new PListReader(initDocumentBuilder().parse(plistStream)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java index 15249c51887..70de6ba92af 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.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 @@ -1172,7 +1172,7 @@ public final class MacSign { "-c", certFile.normalize().toString(), "-k", keychain.name(), "-p", resolvedCertificateRequest.installed().type().verifyPolicy()).saveOutput(!quite).executeWithoutExitCodeCheck(); - if (result.exitCode() == 0) { + if (result.getExitCode() == 0) { return VerifyStatus.VERIFY_OK; } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java index 1f37829791e..9c469c9362e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.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 @@ -22,7 +22,6 @@ */ package jdk.jpackage.test; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.MacSign.DigestAlgorithm.SHA256; import java.nio.file.Path; @@ -30,10 +29,8 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HexFormat; import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.regex.Pattern; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.test.MacSign.CertificateHash; @@ -66,7 +63,7 @@ public final class MacSignVerify { }); // Set to "null" if the sign origin is not found, instead of bailing out with an exception. - // Let is fail in the following TKit.assertEquals() call with a proper log message. + // Let it fail in the following TKit.assertEquals() call with a proper log message. var signOrigin = findSpctlSignOrigin(SpctlType.EXEC, bundleRoot).orElse(null); TKit.assertEquals(certRequest.name(), signOrigin, @@ -92,10 +89,14 @@ public final class MacSignVerify { } public static Optional findEntitlements(Path path) { - final var exec = Executor.of("/usr/bin/codesign", "-d", "--entitlements", "-", "--xml", path.toString()).saveOutput().dumpOutput(); + final var exec = Executor.of( + "/usr/bin/codesign", + "-d", + "--entitlements", "-", + "--xml", path.toString()).saveOutput().dumpOutput().binaryOutput(); final var result = exec.execute(); - var xml = result.stdout().getOutput(); - if (xml.isEmpty()) { + var xml = result.byteStdout(); + if (xml.length == 0) { return Optional.empty(); } else { return Optional.of(MacHelper.readPList(xml)); @@ -135,17 +136,33 @@ public final class MacSignVerify { public static final String ADHOC_SIGN_ORIGIN = "-"; public static Optional findSpctlSignOrigin(SpctlType type, Path path) { - final var exec = Executor.of("/usr/sbin/spctl", "-vv", "--raw", "--assess", "--type", type.value(), path.toString()).saveOutput().discardStderr(); - final var result = exec.executeWithoutExitCodeCheck(); - TKit.assertTrue(Set.of(0, 3).contains(result.exitCode()), - String.format("Check exit code of command %s is either 0 or 3", exec.getPrintableCommandLine())); - return toSupplier(() -> { - try { - return Optional.of(new PListReader(String.join("", result.getOutput()).getBytes()).queryValue("assessment:originator")); - } catch (NoSuchElementException ex) { - return Optional.empty(); + return findSpctlSignOrigin(type, path, false); + } + + public static Optional findSpctlSignOrigin(SpctlType type, Path path, boolean acceptBrokenSignature) { + final var exec = Executor.of( + "/usr/sbin/spctl", + "-vv", + "--raw", + "--assess", + "--type", type.value(), + path.toString()).saveOutput().discardStderr().binaryOutput(); + Executor.Result result; + if (acceptBrokenSignature) { + result = exec.executeWithoutExitCodeCheck(); + switch (result.getExitCode()) { + case 0, 3 -> { + // NOP + } + default -> { + // No plist XML to process. + return Optional.empty(); + } } - }).get(); + } else { + result = exec.execute(0, 3); + } + return MacHelper.readPList(result.byteStdout()).findValue("assessment:originator"); } public static Optional findCodesignSignOrigin(Path path) { @@ -173,7 +190,7 @@ public final class MacSignVerify { } else if (result.getExitCode() == 1 && result.getFirstLineOfOutput().endsWith("code object is not signed at all")) { return Optional.empty(); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); return null; // Unreachable } } @@ -205,7 +222,7 @@ public final class MacSignVerify { TKit.trace("Try /usr/bin/codesign again with `sudo`"); assertSigned(path, true); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); } } @@ -264,13 +281,13 @@ public final class MacSignVerify { return signIdentities; } catch (Exception ex) { ex.printStackTrace(); - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); return null; // Unreachable } } else if (result.getExitCode() == 1 && result.getOutput().getLast().endsWith("Status: no signature")) { return List.of(); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); return null; // Unreachable } } @@ -282,14 +299,13 @@ public final class MacSignVerify { } } - private static void reportUnexpectedCommandOutcome(String printableCommandLine, Executor.Result result) { - Objects.requireNonNull(printableCommandLine); + private static void reportUnexpectedCommandOutcome(Executor.Result result) { Objects.requireNonNull(result); TKit.trace(String.format("Command %s exited with exit code %d and the following output:", - printableCommandLine, result.getExitCode())); + result.getPrintableCommandLine(), result.getExitCode())); result.getOutput().forEach(TKit::trace); TKit.trace("Done"); - TKit.assertUnexpected(String.format("Outcome of command %s", printableCommandLine)); + TKit.assertUnexpected(String.format("Outcome of command %s", result.getPrintableCommandLine())); } private static final Pattern SIGN_IDENTITY_NAME_REGEXP = Pattern.compile("^\\s+\\d+\\.\\s+(.*)$"); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java index fee5b65c897..c0bc858eb1e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,24 @@ import static java.util.stream.Collectors.toCollection; import static java.util.stream.Collectors.toMap; import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Comparator; import java.util.Deque; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingRunnable; public final class Main { @@ -47,10 +54,52 @@ public final class Main { } public static void main(TestBuilder.Builder builder, String... args) throws Exception { + Objects.requireNonNull(builder); + + var argList = List.of(args); + + var ignoreLogfile = argList.contains(CMDLINE_ARG_PREFIX + "ignore-logfile"); + + List filteredArgs; + if (ignoreLogfile) { + filteredArgs = argList.stream().filter(Predicate.isEqual(CMDLINE_ARG_PREFIX + "ignore-logfile").negate()).toList(); + } else { + filteredArgs = argList; + } + + ThrowingRunnable workload = () -> { + run(builder, filteredArgs); + }; + + try { + Optional.ofNullable(TKit.getConfigProperty("logfile")).filter(_ -> { + return !ignoreLogfile; + }).map(Path::of).ifPresentOrElse(logfile -> { + + try (var out = new PrintStream( + Files.newOutputStream(logfile, StandardOpenOption.CREATE, StandardOpenOption.APPEND), + true, + System.out.charset())) { + + TKit.withOutput(workload, out, out); + + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + }, () -> { + ThrowingRunnable.toRunnable(workload).run(); + }); + } catch (Exception ex) { + throw ExceptionBox.unbox(ex); + } + } + + private static void run(TestBuilder.Builder builder, List args) throws Exception { boolean listTests = false; List tests = new ArrayList<>(); try (TestBuilder testBuilder = builder.testConsumer(tests::add).create()) { - Deque argsAsList = new ArrayDeque<>(List.of(args)); + Deque argsAsList = new ArrayDeque<>(args); while (!argsAsList.isEmpty()) { var arg = argsAsList.pop(); TestBuilder.trace(String.format("Parsing [%s]...", arg)); @@ -115,7 +164,7 @@ public final class Main { return; } - TKit.withExtraLogStream(() -> runTests(orderedTests)); + runTests(orderedTests); } private static void runTests(List tests) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index 2e4f11d056f..2baf6683fdf 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -726,12 +726,30 @@ public final class PackageTest extends RunnablePackageTest { } case CREATE -> { - Executor.Result result = cmd.execute(expectedJPackageExitCode); + var nullableOutputBundle = cmd.nullableOutputBundle(); + + var oldOutputBundleSnapshot = nullableOutputBundle + .filter(Files::exists) + .filter(_ -> { + return !cmd.isRemoveOldOutputBundle(); + }) + .map(TKit.PathSnapshot::new); + + var result = cmd.execute(expectedJPackageExitCode); + if (expectedJPackageExitCode == 0) { TKit.assertFileExists(cmd.outputBundle()); } else { - cmd.nullableOutputBundle().ifPresent(outputBundle -> { - TKit.assertPathExists(outputBundle, false); + nullableOutputBundle.ifPresent(outputBundle -> { + oldOutputBundleSnapshot.ifPresentOrElse(snapshot -> { + // jpackage failed, but the output bundle exists. + // This output bundle existed before the jpackage was invoked. + // Verify jpackage didn't modify it. + new TKit.PathSnapshot(outputBundle).assertEquals(snapshot, String.format( + "Check jpackage didn't modify the old output bundle [%s]", outputBundle)); + }, () -> { + TKit.assertPathExists(outputBundle, false); + }); }); } verifyPackageBundle(cmd, result, expectedJPackageExitCode); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 47ab838f8d6..1639beadb28 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; @@ -52,6 +51,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -109,56 +109,45 @@ public final class TKit { throw throwUnknownPlatformError(); }).get(); - static void withExtraLogStream(ThrowingRunnable action) { - if (state().extraLogStream != null) { - ThrowingRunnable.toRunnable(action).run(); - } else { - try (PrintStream logStream = openLogStream()) { - withExtraLogStream(action, logStream); + public static void withOutput(ThrowingRunnable action, PrintStream out, PrintStream err) { + Objects.requireNonNull(action); + Objects.requireNonNull(out); + Objects.requireNonNull(err); + + try { + withState(action, stateBuilder -> { + stateBuilder.out(out).err(err); + }); + } finally { + try { + out.flush(); + } finally { + err.flush(); } } } - static void withExtraLogStream(ThrowingRunnable action, PrintStream logStream) { - withNewState(action, stateBuilder -> { - stateBuilder.extraLogStream(logStream); - }); - } - - public static void withMainLogStream(ThrowingRunnable action, PrintStream logStream) { - withNewState(action, stateBuilder -> { - stateBuilder.mainLogStream(logStream); - }); - } - - public static void withStackTraceStream(ThrowingRunnable action, PrintStream logStream) { - withNewState(action, stateBuilder -> { - stateBuilder.stackTraceStream(logStream); - }); - } - - public static State state() { - return STATE.get(); - } - - public static void state(State v) { - STATE.set(Objects.requireNonNull(v)); - } - - private static void withNewState(ThrowingRunnable action, Consumer stateBuilderMutator) { + public static void withState(ThrowingRunnable action, Consumer stateBuilderMutator) { Objects.requireNonNull(action); Objects.requireNonNull(stateBuilderMutator); - var oldState = state(); - var builder = oldState.buildCopy(); - stateBuilderMutator.accept(builder); - var newState = builder.create(); - try { - state(newState); - ThrowingRunnable.toRunnable(action).run(); - } finally { - state(oldState); - } + var stateBuilder = state().buildCopy(); + stateBuilderMutator.accept(stateBuilder); + withState(action, stateBuilder.create()); + } + + public static void withNewState(ThrowingRunnable action) { + withState(action, _ -> {}); + } + + public static void withState(ThrowingRunnable action, State state) { + Objects.requireNonNull(action); + Objects.requireNonNull(state); + ScopedValue.where(STATE, state).run(ThrowingRunnable.toRunnable(action)); + } + + public static State state() { + return STATE.orElse(DEFAULT_STATE); } enum RunTestMode { @@ -178,33 +167,19 @@ public final class TKit { throw new IllegalStateException("Unexpected nested Test.run() call"); } - withExtraLogStream(() -> { - tests.stream().forEach(test -> { - withNewState(() -> { - try { - if (modes.contains(RunTestMode.FAIL_FAST)) { - test.run(); - } else { - ignoreExceptions(test).run(); - } - } finally { - Optional.ofNullable(state().extraLogStream).ifPresent(PrintStream::flush); - } - }, stateBuilder -> { - stateBuilder.currentTest(test); - }); + tests.stream().forEach(test -> { + withState(() -> { + if (modes.contains(RunTestMode.FAIL_FAST)) { + test.run(); + } else { + ignoreExceptions(test).run(); + } + }, stateBuilder -> { + stateBuilder.currentTest(test); }); }); } - static T runAdhocTest(ThrowingSupplier action) { - final List box = new ArrayList<>(); - runAdhocTest(() -> { - box.add(action.get()); - }); - return box.getFirst(); - } - static void runAdhocTest(ThrowingRunnable action) { Objects.requireNonNull(action); @@ -281,10 +256,7 @@ public final class TKit { static void log(String v) { v = addTimestamp(v); var state = state(); - state.mainLogStream.println(v); - if (state.extraLogStream != null) { - state.extraLogStream.println(v); - } + state.out.println(v); } static Path removeRootFromAbsolutePath(Path v) { @@ -692,8 +664,7 @@ public final class TKit { static void printStackTrace(Throwable throwable) { var state = state(); - Optional.ofNullable(state.extraLogStream).ifPresent(throwable::printStackTrace); - throwable.printStackTrace(state.stackTraceStream); + throwable.printStackTrace(state.err); } private static String concatMessages(String msg, String msg2) { @@ -1255,16 +1226,6 @@ public final class TKit { return new TextStreamVerifier(what); } - private static PrintStream openLogStream() { - return state().logFile.map(logfile -> { - try { - return Files.newOutputStream(logfile, StandardOpenOption.CREATE, StandardOpenOption.APPEND); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - }).map(PrintStream::new).orElse(null); - } - public record PathSnapshot(List contentHashes) { public PathSnapshot { contentHashes.forEach(Objects::requireNonNull); @@ -1376,25 +1337,23 @@ public final class TKit { public static final class State { private State( - Optional logFile, TestInstance currentTest, - PrintStream mainLogStream, - PrintStream stackTraceStream, - PrintStream extraLogStream, + PrintStream out, + PrintStream err, + Map properties, boolean trace, boolean traceAsserts, boolean verboseJPackage, boolean verboseTestSetup) { - Objects.requireNonNull(logFile); - Objects.requireNonNull(mainLogStream); - Objects.requireNonNull(stackTraceStream); + Objects.requireNonNull(out); + Objects.requireNonNull(err); + Objects.requireNonNull(properties); - this.logFile = logFile; this.currentTest = currentTest; - this.mainLogStream = mainLogStream; - this.stackTraceStream = stackTraceStream; - this.extraLogStream = extraLogStream; + this.out = out; + this.err = err; + this.properties = Collections.synchronizedMap(properties); this.trace = trace; this.traceAsserts = traceAsserts; @@ -1403,11 +1362,30 @@ public final class TKit { this.verboseTestSetup = verboseTestSetup; } - Builder buildCopy() { return build().initFrom(this); } + PrintStream out() { + return out; + } + + PrintStream err() { + return err; + } + + Optional findProperty(Object key) { + return Optional.ofNullable(properties.get(Objects.requireNonNull(key))); + } + + void setProperty(Object key, Object value) { + if (value == null) { + properties.remove(Objects.requireNonNull(key)); + } else { + properties.put(Objects.requireNonNull(key), value); + } + } + static Builder build() { return new Builder(); } @@ -1416,11 +1394,9 @@ public final class TKit { static final class Builder { Builder initDefaults() { - logFile = Optional.ofNullable(getConfigProperty("logfile")).map(Path::of); currentTest = null; - mainLogStream = System.out; - stackTraceStream = System.err; - extraLogStream = null; + out = System.out; + err = System.err; var logOptions = tokenizeConfigProperty("suppress-logging"); if (logOptions == null) { @@ -1444,15 +1420,17 @@ public final class TKit { verboseTestSetup = isNonOf.test(Set.of("init", "i")); } + mutable = true; + return this; } Builder initFrom(State state) { - logFile = state.logFile; currentTest = state.currentTest; - mainLogStream = state.mainLogStream; - stackTraceStream = state.stackTraceStream; - extraLogStream = state.extraLogStream; + out = state.out; + err = state.err; + properties.clear(); + properties.putAll(state.properties); trace = state.trace; traceAsserts = state.traceAsserts; @@ -1463,54 +1441,67 @@ public final class TKit { return this; } - Builder logFile(Optional v) { - logFile = v; - return this; - } - Builder currentTest(TestInstance v) { currentTest = v; return this; } - Builder mainLogStream(PrintStream v) { - mainLogStream = v; + Builder out(PrintStream v) { + out = v; return this; } - Builder stackTraceStream(PrintStream v) { - stackTraceStream = v; + Builder err(PrintStream v) { + err = v; return this; } - Builder extraLogStream(PrintStream v) { - extraLogStream = v; + Builder property(Object key, Object value) { + if (value == null) { + properties.remove(Objects.requireNonNull(key)); + } else { + properties.put(Objects.requireNonNull(key), value); + } + return this; + } + + Builder mutable(boolean v) { + mutable = v; return this; } State create() { - return new State(logFile, currentTest, mainLogStream, stackTraceStream, extraLogStream, trace, traceAsserts, verboseJPackage, verboseTestSetup); + return new State( + currentTest, + out, + err, + mutable ? new HashMap<>(properties) : Map.copyOf(properties), + trace, + traceAsserts, + verboseJPackage, + verboseTestSetup); } - private Optional logFile; private TestInstance currentTest; - private PrintStream mainLogStream; - private PrintStream stackTraceStream; - private PrintStream extraLogStream; + private PrintStream out; + private PrintStream err; + private Map properties = new HashMap<>(); private boolean trace; private boolean traceAsserts; private boolean verboseJPackage; private boolean verboseTestSetup; + + private boolean mutable = true; } - private final Optional logFile; private final TestInstance currentTest; - private final PrintStream mainLogStream; - private final PrintStream stackTraceStream; - private final PrintStream extraLogStream; + private final PrintStream out; + private final PrintStream err; + + private final Map properties; private final boolean trace; private final boolean traceAsserts; @@ -1520,10 +1511,6 @@ public final class TKit { } - private static final InheritableThreadLocal STATE = new InheritableThreadLocal<>() { - @Override - protected State initialValue() { - return State.build().initDefaults().create(); - } - }; + private static final ScopedValue STATE = ScopedValue.newInstance(); + private static final State DEFAULT_STATE = State.build().initDefaults().mutable(false).create(); } 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 72b5dbc578b..c7b55ed1de7 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,8 +42,6 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingRunnable; import jdk.jpackage.test.PackageTest.PackageHandlers; @@ -81,16 +79,16 @@ public class WindowsHelper { msiLog.ifPresent(v -> misexec.clearArguments().addArguments(origArgs).addArgument("/L*v").addArgument(v)); result = misexec.executeWithoutExitCodeCheck(); - if (result.exitCode() == 1605) { + if (result.getExitCode() == 1605) { // ERROR_UNKNOWN_PRODUCT, attempt to uninstall not installed // package - return result.exitCode(); + return result.getExitCode(); } // The given Executor may either be of an msiexec command or an // unpack.bat script containing the msiexec command. In the later // case, when misexec returns 1618, the unpack.bat may return 1603 - if ((result.exitCode() == 1618) || (result.exitCode() == 1603 && isUnpack)) { + if ((result.getExitCode() == 1618) || (result.getExitCode() == 1603 && isUnpack)) { // Another installation is already in progress. // Wait a little and try again. Long timeout = 1000L * (attempt + 3); // from 3 to 10 seconds @@ -100,7 +98,7 @@ public class WindowsHelper { break; } - return result.exitCode(); + return result.getExitCode(); } static PackageHandlers createMsiPackageHandlers(boolean createMsiLog) { @@ -306,91 +304,6 @@ public class WindowsHelper { "Failed to get file description of [%s]", pathToExeFile)); } - public static void killProcess(long pid) { - Executor.of("taskkill", "/F", "/PID", Long.toString(pid)).dumpOutput(true).execute(); - } - - public static void killAppLauncherProcess(JPackageCommand cmd, - String launcherName, int expectedCount) { - var pids = findAppLauncherPIDs(cmd, launcherName); - try { - TKit.assertEquals(expectedCount, pids.length, String.format( - "Check [%d] %s app launcher processes found running", - expectedCount, Optional.ofNullable(launcherName).map( - str -> "[" + str + "]").orElse("
    "))); - } finally { - if (pids.length != 0) { - killProcess(pids[0]); - } - } - } - - private static long[] findAppLauncherPIDs(JPackageCommand cmd, String launcherName) { - // Get the list of PIDs and PPIDs of app launcher processes. Run setWinRunWithEnglishOutput(true) for JDK-8344275. - // powershell -NoLogo -NoProfile -NonInteractive -Command - // "Get-CimInstance Win32_Process -Filter \"Name = 'foo.exe'\" | select ProcessID,ParentProcessID" - String command = "Get-CimInstance Win32_Process -Filter \\\"Name = '" - + cmd.appLauncherPath(launcherName).getFileName().toString() - + "'\\\" | select ProcessID,ParentProcessID"; - List output = Executor.of("powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command) - .dumpOutput(true).saveOutput().setWinRunWithEnglishOutput(true).executeAndGetOutput(); - - if (output.size() < 1) { - return new long[0]; - } - - String[] headers = Stream.of(output.get(1).split("\\s+", 2)).map( - String::trim).map(String::toLowerCase).toArray(String[]::new); - Pattern pattern; - if (headers[0].equals("parentprocessid") && headers[1].equals( - "processid")) { - pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); - } else if (headers[1].equals("parentprocessid") && headers[0].equals( - "processid")) { - pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); - } else { - throw new RuntimeException( - "Unrecognizable output of \'Get-CimInstance Win32_Process\' command"); - } - - List processes = output.stream().skip(3).map(line -> { - Matcher m = pattern.matcher(line); - long[] pids = null; - if (m.matches()) { - pids = new long[]{Long.parseLong(m.group("pid")), Long. - parseLong(m.group("ppid"))}; - } - return pids; - }).filter(Objects::nonNull).toList(); - - switch (processes.size()) { - case 2 -> { - final long parentPID; - final long childPID; - if (processes.get(0)[0] == processes.get(1)[1]) { - parentPID = processes.get(0)[0]; - childPID = processes.get(1)[0]; - } else if (processes.get(1)[0] == processes.get(0)[1]) { - parentPID = processes.get(1)[0]; - childPID = processes.get(0)[0]; - } else { - TKit.assertUnexpected("App launcher processes unrelated"); - return null; // Unreachable - } - return new long[]{parentPID, childPID}; - } - case 1 -> { - return new long[]{processes.get(0)[0]}; - } - default -> { - TKit.assertUnexpected(String.format( - "Unexpected number of running processes [%d]", - processes.size())); - return null; // Unreachable - } - } - } - static boolean isUserLocalInstall(JPackageCommand cmd) { return cmd.hasArgument("--win-per-user-install"); } @@ -462,7 +375,7 @@ public class WindowsHelper { var status = Executor.of("reg", "query", keyPath, "/v", valueName) .saveOutput() .executeWithoutExitCodeCheck(); - if (status.exitCode() == 1) { + if (status.getExitCode() == 1) { // Should be the case of no such registry value or key String lookupString = "ERROR: The system was unable to find the specified registry key or value."; TKit.assertTextStream(lookupString) diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java new file mode 100644 index 00000000000..d9ab38e006a --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test.mock; + +import java.io.PrintStream; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * An action. + */ +@FunctionalInterface +public interface CommandAction { + + public record Context(PrintStream out, PrintStream err, List args) { + + public Context { + Objects.requireNonNull(out); + Objects.requireNonNull(err); + args.forEach(Objects::requireNonNull); + } + + public Optional findOptionValue(String option) { + Objects.requireNonNull(option); + var idx = args.indexOf(option); + if (idx >= 0 && idx + 1 < args.size()) { + return Optional.of(args.get(idx + 1)); + } else { + return Optional.empty(); + } + } + + public String optionValue(String option) { + return findOptionValue(option).orElseThrow(() -> { + throw new MockIllegalStateException(String.format("No option %s", option)); + }); + } + + public MockIllegalStateException unexpectedArguments() { + return new MockIllegalStateException(String.format("Unexpected arguments: %s", args)); + } + } + + /** + * Runs the action in the given context. + * + * @param context the context + * @return an {@code Optional} wrapping the exit code, indicating it is the last + * action in the sequence or an empty {@code Optional} otherwise + * @throws Exception simulates a failure + * @throws MockIllegalStateException if error in internal mock logic occurred. + * E.g.: if the action was called unexpectedly + */ + Optional run(Context context) throws Exception, MockIllegalStateException; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java new file mode 100644 index 00000000000..25814d84205 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java @@ -0,0 +1,86 @@ +/* + * 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.test.mock; + +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.internal.util.function.ThrowingRunnable; + +/** + * Specification of a {@link CommandAction}. + *

    + * Comprised of a human-readable description and an associated action. + */ +public interface CommandActionSpec { + + String description(); + CommandAction action(); + + public static CommandActionSpec create(String description, CommandAction action) { + return new Internal.DefaultCommandActionSpec(description, action); + } + + public static CommandActionSpec create(String description, ThrowingSupplier action) { + Objects.requireNonNull(action); + return create(description, _ -> { + return Optional.of(action.get()); + }); + } + + public static CommandActionSpec create(String description, ThrowingRunnable action) { + Objects.requireNonNull(action); + return create(description, _ -> { + action.run(); + return Optional.empty(); + }); + } + + @SuppressWarnings("overloads") + public static CommandActionSpec create(String description, ThrowingConsumer action) { + Objects.requireNonNull(action); + return create(description, context -> { + action.accept(context); + return Optional.empty(); + }); + } + + final class Internal { + + private Internal() { + } + + private record DefaultCommandActionSpec(String description, CommandAction action) implements CommandActionSpec { + DefaultCommandActionSpec { + Objects.requireNonNull(description); + Objects.requireNonNull(action); + } + + @Override + public String toString() { + return description(); + } + } + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java new file mode 100644 index 00000000000..ce3aa6f80e1 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java @@ -0,0 +1,192 @@ +/* + * 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.test.mock; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; + +/** + * A sequence of actions. + */ +public record CommandActionSpecs(List specs) { + + public CommandActionSpecs { + Objects.requireNonNull(specs); + } + + public CommandActionSpecs andThen(CommandActionSpecs other) { + return build().append(this).append(other).create(); + } + + public Stream actions() { + return specs.stream().map(CommandActionSpec::action); + } + + public CommandMock.Builder toCommandMockBuilder() { + return new CommandMock.Builder().mutate(builder -> { + builder.actions().append(this); + }); + } + + @Override + public String toString() { + return specs.toString(); + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + public CommandActionSpecs create() { + return new CommandActionSpecs(List.copyOf(specs)); + } + + public Builder stdout(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s>>1", content), context -> { + var out = context.out(); + content.forEach(out::println); + })); + } + + public Builder stdout(String... str) { + return stdout(List.of(str)); + } + + public Builder stderr(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s>>2", content), context -> { + var err = context.err(); + content.forEach(err::println); + })); + } + + public Builder stderr(String... str) { + return stderr(List.of(str)); + } + + public Builder printToStdout(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s(no-eol)>>1", content), context -> { + var out = context.out(); + content.forEach(out::print); + })); + } + + public Builder printToStdout(String... str) { + return printToStdout(List.of(str)); + } + + public Builder printToStderr(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s(no-eol)>>2", content), context -> { + var err = context.err(); + content.forEach(err::print); + })); + } + + public Builder printToStderr(String... str) { + return printToStderr(List.of(str)); + } + + public Builder argsListener(Consumer> listener) { + Objects.requireNonNull(listener); + return action(CommandActionSpec.create("args-listener", context -> { + listener.accept(context.args()); + })); + } + + public Builder exit(int exitCode) { + return action(CommandActionSpec.create(String.format("exit(%d)", exitCode), () -> { + return exitCode; + })); + } + + public Builder exit() { + return exit(0); + } + + public Builder exit(CommandMockExit exit) { + switch (exit) { + case SUCCEED -> { + return exit(); + } + case EXIT_1 -> { + return exit(1); + } + case THROW_MOCK_IO_EXCEPTION -> { + return action(CommandActionSpec.create("", () -> { + throw new MockingToolProvider.RethrowableException(new MockIOException("Kaput!")); + })); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + public Builder append(Builder other) { + return append(other.specs); + } + + public Builder append(CommandActionSpecs other) { + return append(other.specs()); + } + + public Builder append(List other) { + specs.addAll(other); + return this; + } + + public Builder action(CommandActionSpec v) { + specs.add(Objects.requireNonNull(v)); + return this; + } + + public Builder copy() { + return new Builder().append(this); + } + + public CommandMock.Builder toCommandMockBuilder() { + return new CommandMock.Builder().mutate(builder -> { + builder.actions(this); + }); + } + + private final List specs = new ArrayList<>(); + } + + public static final CommandActionSpecs UNREACHABLE = new CommandActionSpecs(List.of()); +} + diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java new file mode 100644 index 00000000000..bb3980fd4e0 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test.mock; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.IntStream; + +/** + * Command mock. + */ +public sealed interface CommandMock permits ToolProviderCommandMock, VerbatimCommandMock, CompletableCommandMock { + + public static CommandMock ioerror(String name) { + return CommandActionSpecs.build() + .exit(CommandMockExit.THROW_MOCK_IO_EXCEPTION) + .toCommandMockBuilder().name(Objects.requireNonNull(name)).create(); + } + + public static CommandMock fail(String name) { + return CommandActionSpecs.build() + .exit(CommandMockExit.EXIT_1) + .toCommandMockBuilder().name(Objects.requireNonNull(name)).create(); + } + + public static CommandMock succeed(String name) { + return CommandActionSpecs.build() + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name(Objects.requireNonNull(name)).create(); + } + + public static CommandMock unreachable() { + return MockingToolProvider.UNREACHABLE; + } + + public final class Builder { + + public ToolProviderCommandMock create() { + + var actionSpecs = Optional.ofNullable(scriptBuilder) + .map(CommandActionSpecs.Builder::create) + .orElse(CommandActionSpecs.UNREACHABLE); + if (actionSpecs.equals(CommandActionSpecs.UNREACHABLE)) { + return (ToolProviderCommandMock)unreachable(); + } + + var theName = Optional.ofNullable(name).orElse("mock"); + var script = actionSpecs.actions().toList(); + switch (repeat) { + case 0 -> { + return MockingToolProvider.create(theName, script); + } + case -1 -> { + return MockingToolProvider.createLoop(theName, script); + } + default -> { + var repeatedScript = IntStream.rangeClosed(0, repeat) + .mapToObj(i -> script) + .flatMap(List::stream) + .toList(); + return MockingToolProvider.create(theName, repeatedScript); + } + } + } + + public Builder name(String v) { + name = v; + return this; + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + public Builder repeat(int v) { + repeat = Integer.max(-1, v); + return this; + } + + public Builder noRepeats() { + return repeat(0); + } + + public Builder repeatInfinitely() { + return repeat(-1); + } + + public Builder actions(CommandActionSpecs.Builder v) { + scriptBuilder = Optional.ofNullable(v).orElseGet(CommandActionSpecs::build); + return this; + } + + public CommandActionSpecs.Builder actions() { + if (scriptBuilder == null) { + scriptBuilder = CommandActionSpecs.build(); + } + return scriptBuilder; + } + + private String name; + private int repeat = -1; + private CommandActionSpecs.Builder scriptBuilder; + } + +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.java new file mode 100644 index 00000000000..26bc8ba757b --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.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 jdk.jpackage.test.mock; + +import jdk.jpackage.internal.util.CommandOutputControl; + +public enum CommandMockExit { + /** + * Exit normally with "0" exit code. + */ + SUCCEED(true, true), + /** + * Exit normally with "1" exit code. + */ + EXIT_1(false, true), + /** + * Throw {@link MockIOException}. This simulates a situation when an I/O error + * occurs starting a subprocess with {@link ProcessBuilder#start()}. + * {@link CommandOutputControl.Executable#execute()} will handle I/O errors and + * let them out. + */ + THROW_MOCK_IO_EXCEPTION(false, false), + ; + + CommandMockExit(boolean succeed, boolean exitNormally) { + this.succeed = succeed; + this.exitNormally = exitNormally; + } + + public boolean succeed() { + return succeed; + } + + public boolean exitNormally() { + return exitNormally; + } + + private final boolean succeed; + private final boolean exitNormally; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java new file mode 100644 index 00000000000..2572309e751 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java @@ -0,0 +1,63 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.Objects; + +/** + * Specification of a {@link CommandMock}. + */ +public record CommandMockSpec(Path name, Path mockName, CommandActionSpecs actions) { + + public CommandMockSpec { + Objects.requireNonNull(name); + Objects.requireNonNull(mockName); + Objects.requireNonNull(actions); + } + + public CommandMockSpec(Path name, CommandActionSpecs actions) { + this(name, Path.of(name.toString() + "-mock"), actions); + } + + public CommandMockSpec(String name, CommandActionSpecs actions) { + this(Path.of(name), actions); + } + + public CommandMockSpec(String name, String mockName, CommandActionSpecs actions) { + this(Path.of(name), Path.of(mockName), actions); + } + + public CommandMock.Builder toCommandMockBuilder() { + return actions.toCommandMockBuilder().name(mockName.toString()); + } + + public boolean isDefaultMockName() { + return (name.getFileName().toString() + "-mock").equals(mockName.getFileName().toString()); + } + + @Override + public String toString() { + return String.format("mock-of(%s)%s", name, actions); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java new file mode 100644 index 00000000000..c9441e038b3 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java @@ -0,0 +1,31 @@ +/* + * 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.test.mock; + +/** + * Command mock that runs a finite sequence of actions. + */ +public sealed interface CompletableCommandMock extends CommandMock permits ToolProviderCompletableCommandMock { + + boolean completed(); +} diff --git a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java similarity index 56% rename from test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java rename to test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java index b0ba6c60854..3b299f05d3f 100644 --- a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, 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 @@ -20,23 +20,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package jdk.jpackage.test.mock; -/* - * @test - * @bug 4303068 - * @summary be allowed to specify the security properties file - * as a -D system property +import java.io.IOException; + +/** + * Simulates I/O error. * - * @run main/othervm -Djava.security.properties=${test.src}/SecurityPropFile.file -Djava.security.debug=properties SecurityPropFile + * @see CommandMockExit#THROW_MOCK_IO_EXCEPTION */ +public final class MockIOException extends IOException { -public class SecurityPropFile { - public static void main(String[] args) { - System.out.println(java.security.Security.getProperty - ("policy.provider")); - System.out.println(java.security.Security.getProperty - ("policy.url.1")); - System.out.println(java.security.Security.getProperty - ("policy.url.2")); + MockIOException(String msg) { + super(msg); } + + private static final long serialVersionUID = 1L; } diff --git a/src/hotspot/share/runtime/prefetch.hpp b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java similarity index 53% rename from src/hotspot/share/runtime/prefetch.hpp rename to test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java index 601337c14af..1817587364a 100644 --- a/src/hotspot/share/runtime/prefetch.hpp +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -19,27 +19,17 @@ * 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.test.mock; -#ifndef SHARE_RUNTIME_PREFETCH_HPP -#define SHARE_RUNTIME_PREFETCH_HPP +/** + * Indicates command mock internal error. + */ +public final class MockIllegalStateException extends IllegalStateException { -#include "memory/allStatic.hpp" + public MockIllegalStateException(String msg) { + super(msg); + } -// If calls to prefetch methods are in a loop, the loop should be cloned -// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval -// say not to do prefetching, these methods aren't called. At the very -// least, they take up a memory issue slot. They should be implemented -// as inline assembly code: doing an actual call isn't worth the cost. - -class Prefetch : AllStatic { - public: - // Prefetch anticipating read; must not fault, semantically a no-op - static void read(const void* loc, intx interval); - - // Prefetch anticipating write; must not fault, semantically a no-op - static void write(void* loc, intx interval); -}; - -#endif // SHARE_RUNTIME_PREFETCH_HPP + private static final long serialVersionUID = 1L; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java new file mode 100644 index 00000000000..f8c04cc3927 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java @@ -0,0 +1,164 @@ +/* + * 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.test.mock; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import jdk.jpackage.internal.util.function.ExceptionBox; + +/** + * A command simulator implementing {@code ToolProvider}. + *

    + * Iterates over actions and runs them. Each action is write to stdout/stderr, create a file, etc. + */ +abstract sealed class MockingToolProvider implements ToolProviderCommandMock { + + MockingToolProvider(String name, Iterator actionIter) { + this.name = Objects.requireNonNull(name); + this.actionIter = Objects.requireNonNull(actionIter); + } + + static ToolProviderCommandMock createLoop(String name, Iterable actions) { + return new MockingToolProvider.NonCompletable(name, actions); + } + + static MockingToolProvider create(String name, Iterable actions) { + return new MockingToolProvider.Completable(name, actions); + } + + public boolean completed() { + return !actionIter.hasNext(); + } + + @Override + public String name() { + return name; + } + + @Override + public int run(PrintStream out, PrintStream err, String... args) { + var context = new CommandAction.Context(out, err, List.of(args)); + try { + while (actionIter.hasNext()) { + var action = actionIter.next(); + var reply = action.run(context); + if (reply.isPresent()) { + return reply.get(); + } + } + } catch (RethrowableException ex) { + // Let the checked exception out. + throwAny(ex.getCause()); + // Unreachable + return 0; + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); + } + + // No more actions to execute, but still expect it to keep going. + throw new MockIllegalStateException("No more actions to execute"); + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw new UnsupportedOperationException(); + } + + static final class RethrowableException extends Exception { + + RethrowableException(Exception ex) { + super(Objects.requireNonNull(ex)); + } + + private static final long serialVersionUID = 1L; + } + + @SuppressWarnings("unchecked") + private static void throwAny(Throwable e) throws E { + throw (E)e; + } + + private static final class LoopIterator implements Iterator { + + LoopIterator(Iterable iterable) { + this.iterable = Objects.requireNonNull(iterable); + rewind(); + } + + @Override + public boolean hasNext() { + return iter != null; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } else if (iter.hasNext()) { + return iter.next(); + } else { + rewind(); + if (!hasNext()) { + throw new NoSuchElementException(); + } else { + return iter.next(); + } + } + } + + private void rewind() { + iter = Objects.requireNonNull(iterable.iterator()); + if (!iter.hasNext()) { + iter = null; + } + } + + private final Iterable iterable; + private Iterator iter; + } + + static final class NonCompletable extends MockingToolProvider { + + NonCompletable(String name, Iterable actions) { + super(name, new LoopIterator<>(actions)); + } + + } + + static final class Completable extends MockingToolProvider implements ToolProviderCompletableCommandMock { + + Completable(String name, Iterable actions) { + super(name, actions.iterator()); + } + + } + + private final String name; + private final Iterator actionIter; + + static ToolProviderCommandMock UNREACHABLE = new MockingToolProvider.NonCompletable("", List.of()); +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java new file mode 100644 index 00000000000..bc51d2b69f8 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java @@ -0,0 +1,297 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.IdentityWrapper; + +/** + * Script of command mocks. + */ +public interface Script { + + /** + * Returns a command mock for the given command line. + * + * @param cmdline the command line for which to look up a command mock + * + * @return a command mock matching the given command line + * @throws ScriptException if an internal script error occures + */ + CommandMock map(List cmdline) throws ScriptException; + + /** + * Returns command mocks registered with this object that have not completed yet. + * + * @See {@link CompletableCommandMock#completed()} + * + * @return the command mocks registered with this object that have not completed yet + */ + Collection incompleteMocks(); + + public static Builder build() { + return new Builder(); + } + + public static Predicate> cmdlinePredicate( + Predicate pred, + Function conv, + Function, Stream> toStream) { + + Objects.requireNonNull(pred); + Objects.requireNonNull(conv); + Objects.requireNonNull(toStream); + + return cmdline -> { + return toStream.apply(cmdline).map(conv).filter(pred).findFirst().isPresent(); + }; + } + + public static Predicate> cmdlineContains(String arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), x -> x, List::stream); + } + + public static Predicate> cmdlineContains(Path arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), Path::of, List::stream); + } + + public static Predicate> cmdlineStartsWith(String arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), x -> x, cmdline -> { + return cmdline.stream().limit(1); + }); + } + + public static Predicate> cmdlineStartsWith(Path arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), Path::of, cmdline -> { + return cmdline.stream().limit(1); + }); + } + + public final class ScriptException extends RuntimeException { + + ScriptException(RuntimeException cause) { + super(Objects.requireNonNull(cause)); + } + + ScriptException(String msg) { + super(Objects.requireNonNull(msg)); + } + + private static final long serialVersionUID = 1L; + } + + public final class Builder { + + public Script createSequence() { + return new SequenceScript(List.copyOf(instructions), completableMocks()); + } + + public Script createLoop() { + return new LoopScript(List.copyOf(instructions), completableMocks()); + } + + public Builder map(Predicate> pred, CommandMock mock) { + Objects.requireNonNull(pred); + Objects.requireNonNull(mock); + if (mock instanceof CompletableCommandMock completable) { + completableMocks.add(new IdentityWrapper<>(completable)); + } + instruction(cmdline -> { + if (pred.test(cmdline)) { + return new CommandMockResult(Optional.of(mock)); + } else { + return new CommandMockResult(Optional.empty()); + } + }); + return this; + } + + public Builder map(Predicate> pred, CommandMock.Builder mock) { + Optional.ofNullable(commandMockBuilderMutator).ifPresent(mock::mutate); + return map(pred, mock.create()); + } + + public Builder map(Predicate> pred, CommandMockSpec mock) { + return map(pred, mock.toCommandMockBuilder()); + } + + public Builder map(CommandMockSpec mock) { + return map(cmdlineStartsWith(mock.name()), mock.toCommandMockBuilder()); + } + + public Builder use(CommandMock mock) { + return map(_ -> true, mock); + } + + public Builder use(Predicate> pred, CommandMock.Builder mock) { + return map(_ -> true, mock); + } + + public Builder use(Predicate> pred, CommandMockSpec mock) { + return map(_ -> true, mock); + } + + public Builder branch(Predicate> pred, Script script) { + Objects.requireNonNull(pred); + Objects.requireNonNull(script); + instruction(cmdline -> { + if (pred.test(cmdline)) { + return new ScriptResult(script); + } else { + return new CommandMockResult(Optional.empty()); + } + }); + return this; + } + + public Builder commandMockBuilderMutator(Consumer v) { + commandMockBuilderMutator = v; + return this; + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + private Builder instruction(Function, Result> instruction) { + instructions.add(Objects.requireNonNull(instruction)); + return this; + } + + private Collection completableMocks() { + return completableMocks.stream().map(IdentityWrapper::value).toList(); + } + + private static RuntimeException noMapping(List cmdline) { + return new ScriptException(String.format("Mapping for %s command line not found", cmdline)); + } + + private sealed interface Result { + } + + private record CommandMockResult(Optional value) implements Result { + CommandMockResult { + Objects.requireNonNull(value); + } + } + + private record ScriptResult(Script value) implements Result { + ScriptResult { + Objects.requireNonNull(value); + } + } + + private abstract static class AbstractScript implements Script { + + AbstractScript(Collection completableMocks) { + this.completableMocks = Objects.requireNonNull(completableMocks); + } + + @Override + public Collection incompleteMocks() { + return completableMocks.stream().filter(Predicate.not(CompletableCommandMock::completed)).toList(); + } + + private final Collection completableMocks; + } + + private static final class LoopScript extends AbstractScript { + + LoopScript(List, Result>> instructions, + Collection completableMocks) { + super(completableMocks); + this.instructions = Objects.requireNonNull(instructions); + } + + @Override + public CommandMock map(List cmdline) { + for (var instruction : instructions) { + switch (instruction.apply(cmdline)) { + case CommandMockResult result -> { + var mock = result.value(); + if (mock.isPresent()) { + return mock.get(); + } + } + case ScriptResult result -> { + return result.value().map(cmdline); + } + } + } + + throw noMapping(cmdline); + } + + private final List, Result>> instructions; + } + + private static final class SequenceScript extends AbstractScript { + + SequenceScript(List, Result>> instructions, + Collection completableMocks) { + super(completableMocks); + this.iter = instructions.iterator(); + } + + @Override + public CommandMock map(List cmdline) { + if (!iter.hasNext()) { + throw new ScriptException("No more mappings"); + } else { + switch (iter.next().apply(cmdline)) { + case CommandMockResult result -> { + var mock = result.value(); + if (mock.isPresent()) { + return mock.get(); + } + } + case ScriptResult result -> { + return result.value().map(cmdline); + } + } + } + + throw noMapping(cmdline); + } + + private final Iterator, Result>> iter; + } + + private Consumer commandMockBuilderMutator = CommandMock.Builder::noRepeats; + private final List, Result>> instructions = new ArrayList<>(); + private final Set> completableMocks = new HashSet<>(); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java new file mode 100644 index 00000000000..60e5723e9a7 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java @@ -0,0 +1,179 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * Specification of a {@link Script}. + */ +public record ScriptSpec(List items, boolean loop) { + + public ScriptSpec { + Objects.requireNonNull(items); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + sb.append(items.toString()); + if (loop) { + // Append "Clockwise Gapped Circle Arrow" Unicode symbol. + sb.append('(').appendCodePoint(0x27F3).append(')'); + } + return sb.toString(); + } + + public Script create() { + var script = Script.build(); + items.forEach(item -> { + item.applyTo(script, loop); + }); + if (loop) { + return script.createLoop(); + } else { + return script.createSequence(); + } + } + + public Collection commandNames() { + return items.stream().map(Item::mockSpec).map(CommandMockSpec::name).distinct().toList(); + } + + private record Item(CommandMockSpec mockSpec, int repeatCount, boolean detailedDescription) { + + private Item { + Objects.requireNonNull(mockSpec); + if (repeatCount < 0) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + var sb = new StringBuilder(); + if (detailedDescription) { + sb.append(mockSpec); + } else if (mockSpec.isDefaultMockName()) { + sb.append(mockSpec.name()); + } else { + sb.append(mockSpec.mockName()); + } + if (repeatCount > 0) { + sb.append('(').append(repeatCount + 1).append(')'); + } + return sb.toString(); + } + + void applyTo(Script.Builder script, boolean loopScript) { + var pred = Script.cmdlineStartsWith(mockSpec.name()); + + var mockBuilder = mockSpec.toCommandMockBuilder(); + if (loopScript) { + script.map(pred, mockBuilder.repeat(repeatCount).create()); + } else { + mockBuilder.repeat(0); + IntStream.rangeClosed(0, repeatCount).forEach(_ -> { + script.map(pred, mockBuilder.create()); + }); + } + } + + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + private Builder() { + } + + public ScriptSpec create() { + return new ScriptSpec(List.copyOf(items), loop); + } + + public Builder loop(boolean v) { + loop = v; + return this; + } + + public Builder loop() { + return loop(true); + } + + public final class ItemBuilder { + + private ItemBuilder(CommandMockSpec mockSpec) { + this.mockSpec = Objects.requireNonNull(mockSpec); + } + + public Builder add() { + items.add(new Item(mockSpec, repeat, detailedDescription)); + return Builder.this; + } + + public ItemBuilder repeat(int v) { + if (repeat < 0) { + throw new IllegalArgumentException(); + } + repeat = v; + return this; + } + + public ItemBuilder detailedDescription(boolean v) { + detailedDescription = v; + return this; + } + + public ItemBuilder detailedDescription() { + return detailedDescription(true); + } + + private final CommandMockSpec mockSpec; + private int repeat; + private boolean detailedDescription; + } + + public Builder add(CommandMockSpec mockSpec) { + return build(mockSpec).add(); + } + + public Builder addLoop(CommandMockSpec mockSpec) { + return build(mockSpec).add(); + } + + public ItemBuilder build(CommandMockSpec mockSpec) { + return new ItemBuilder(mockSpec); + } + + private final List items = new ArrayList<>(); + private boolean loop; + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java new file mode 100644 index 00000000000..8c59c777d96 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java @@ -0,0 +1,66 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.Objects; + +/** + * Specification of a {@link Script} bound to a specific directory. + */ +public class ScriptSpecInDir { + + public ScriptSpecInDir() { + } + + @Override + public String toString() { + return scriptSpec.toString(); + } + + public boolean isPathInDir(Path path) { + return path.startsWith(dir); + } + + public ScriptSpecInDir dir(Path v) { + dir = v; + return this; + } + + public ScriptSpecInDir scriptSpec(ScriptSpec v) { + scriptSpec = v; + return this; + } + + public ScriptSpec scriptSpec() { + Objects.requireNonNull(dir); + return Objects.requireNonNull(scriptSpec); + } + + public Script create() { + return scriptSpec().create(); + } + + private ScriptSpec scriptSpec; + private Path dir; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java new file mode 100644 index 00000000000..ee9556277d4 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test.mock; + +import java.util.spi.ToolProvider; + +public sealed interface ToolProviderCommandMock extends CommandMock, ToolProvider + permits ToolProviderCompletableCommandMock, MockingToolProvider { +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java new file mode 100644 index 00000000000..907cedd38d7 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java @@ -0,0 +1,27 @@ +/* + * 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.test.mock; + +public sealed interface ToolProviderCompletableCommandMock extends ToolProviderCommandMock, CompletableCommandMock + permits MockingToolProvider.Completable { +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java new file mode 100644 index 00000000000..799caaa0ea9 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java @@ -0,0 +1,28 @@ +/* + * 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.test.mock; + +public enum VerbatimCommandMock implements CommandMock { + + INSTANCE +} diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java new file mode 100644 index 00000000000..41a9bdc647e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java @@ -0,0 +1,54 @@ +/* + * 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 jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class LibProvidersLookupTest { + + @ParameterizedTest + @EnumSource(value = CommandMockExit.class) + public void test_supported(CommandMockExit exit) { + + var ldd = CommandActionSpecs.build().exit(exit).toCommandMockBuilder().name("ldd-mock").create(); + + Globals.main(() -> { + Globals.instance().executorFactory(() -> { + return new Executor().mapper(executor -> { + return executor.copy().mapper(null).toolProvider(ldd); + }); + }); + + boolean actual = LibProvidersLookup.supported(); + assertEquals(exit.exitNormally(), actual); + + return 0; + }); + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java new file mode 100644 index 00000000000..baf03a32142 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java @@ -0,0 +1,154 @@ +/* + * 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 jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; +import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.Result; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.Script; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class LinuxPackageArchTest { + + @ParameterizedTest + @MethodSource + public void test(Runnable test) { + test.run(); + } + + private static List test() { + var data = new ArrayList(); + + // "foo" stdout interleaved with "bar" stderr + var fooArch = CommandActionSpecs.build() + .printToStdout("f").printToStderr("b") + .printToStdout("o").printToStderr("a") + .printToStdout("o").printToStderr("r"); + + for (var exit : CommandMockExit.values()) { + var dpkg = fooArch.copy().printToStdout("-deb").exit(exit).create(); + + data.add(new DebTestSpec(dpkg, Optional.of("foo-deb").filter(_ -> { + return exit.succeed(); + }))); + } + + for (var rpmbuildExit : CommandMockExit.values()) { + var rpmbuild = fooArch.copy().printToStdout("-rpmbuild").exit(rpmbuildExit).create(); + for (var rpmExit : CommandMockExit.values()) { + var rpm = fooArch.copy().printToStdout("-rpm").exit(rpmExit).create(); + Optional expect; + if (rpmbuildExit.succeed()) { + expect = Optional.of("foo-rpmbuild"); + rpm = CommandActionSpecs.UNREACHABLE; + } else { + if (rpmExit.succeed()) { + expect = Optional.of("foo-rpm"); + } else { + expect = Optional.empty(); + } + } + + data.add(new RpmTestSpec(rpmbuild, rpm, expect)); + } + } + + return data; + } + + record RpmTestSpec(CommandActionSpecs rpmbuild, CommandActionSpecs rpm, Optional expect) implements Runnable { + + RpmTestSpec { + Objects.requireNonNull(rpm); + Objects.requireNonNull(rpmbuild); + Objects.requireNonNull(expect); + } + + @Override + public void run() { + + // Create an executor factory that will: + // - Substitute the "rpm" command with `rpm` mock. + // - Substitute the "rpmbuild" command with `rpmbuild` mock. + // - Throw if a command with the name other than "rpm" and "rpmbuild" is requested for execution. + + var script = Script.build() + // LinuxPackageArch must run the "rpmbuild" command first. Put its mapping at the first position. + .map(new CommandMockSpec("rpmbuild", rpmbuild)) + // LinuxPackageArch may optionally run the "rpm" command. Put its mapping after the "rpmbuild" command mapping. + .map(new CommandMockSpec("rpm", rpm)) + // Create a sequential script: after every Script#map() call, the script will advance the current mapping. + // This means each mapping in the script will be considered only once. + // If "rpm" and "rpmbuild" commands are executed in reverse order, the second Script#map() will throw. + .createSequence(); + + test(expect, LINUX_RPM, script); + } + } + + record DebTestSpec(CommandActionSpecs dpkg, Optional expect) implements Runnable { + + DebTestSpec { + Objects.requireNonNull(dpkg); + Objects.requireNonNull(expect); + } + + @Override + public void run() { + var script = Script.build().map(new CommandMockSpec("dpkg", dpkg)).createSequence(); + + test(expect, LINUX_DEB, script); + } + } + + private static void test(Optional expectedArch, StandardPackageType pkgType, Script script) { + + Globals.main(() -> { + + MockUtils.buildJPackage().script(script).applyToGlobals(); + + Result arch = LinuxPackageArch.create(pkgType); + + assertEquals(arch.hasValue(), expectedArch.isPresent()); + expectedArch.ifPresent(v -> { + assertEquals(v, arch.orElseThrow().value()); + }); + + assertEquals(List.of(), script.incompleteMocks()); + + return 0; + }); + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java new file mode 100644 index 00000000000..8ff958491b1 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.Script; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class LinuxSystemEnvironmentTest { + + @ParameterizedTest + @MethodSource + public void test_detectNativePackageType(DetectNativePackageTypeTestSpec test) { + test.run(); + } + + private static List test_detectNativePackageType() { + var data = new ArrayList(); + for (var rpmExit : CommandMockExit.values()) { + for (var debExit : CommandMockExit.values()) { + CommandActionSpecs deb = CommandActionSpecs.build().exit(debExit).create(); + CommandActionSpecs rpm; + Optional expected; + if (debExit.succeed()) { + expected = Optional.of(StandardPackageType.LINUX_DEB); + rpm = CommandActionSpecs.UNREACHABLE; + } else { + rpm = CommandActionSpecs.build().exit(rpmExit).create(); + if (rpmExit.succeed()) { + expected = Optional.of(StandardPackageType.LINUX_RPM); + } else { + expected = Optional.empty(); + } + } + data.add(new DetectNativePackageTypeTestSpec(expected, rpm, deb)); + } + } + return data; + } + + record DetectNativePackageTypeTestSpec(Optional expect, CommandActionSpecs rpm, CommandActionSpecs deb) { + + DetectNativePackageTypeTestSpec { + Objects.requireNonNull(expect); + Objects.requireNonNull(rpm); + Objects.requireNonNull(deb); + } + + void run() { + + var script = Script.build() + .map(new CommandMockSpec("rpm", rpm)) + .map(new CommandMockSpec("dpkg", deb)) + .createLoop(); + + Globals.main(() -> { + + MockUtils.buildJPackage().script(script).applyToGlobals(); + + var actual = LinuxSystemEnvironment.detectNativePackageType(); + + assertEquals(expect, actual); + + assertEquals(List.of(), script.incompleteMocks()); + + return 0; + }); + } + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/junit.java b/test/jdk/tools/jpackage/junit/linux/junit.java index 214812e951e..0fd337c812c 100644 --- a/test/jdk/tools/jpackage/junit/linux/junit.java +++ b/test/jdk/tools/jpackage/junit/linux/junit.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 @@ -30,3 +30,35 @@ * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxApplicationLayoutTest */ + +/* @test + * @summary Test LinuxSystemEnvironment + * @requires (os.family == "linux") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LinuxSystemEnvironmentTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxSystemEnvironmentTest + */ + +/* @test + * @summary Test LibProvidersLookup + * @requires (os.family == "linux") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LibProvidersLookupTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LibProvidersLookupTest + */ + +/* @test + * @summary Test LinuxPackageArch + * @requires (os.family == "linux") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LinuxPackageArchTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxPackageArchTest + */ diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java new file mode 100644 index 00000000000..e5da383142a --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java @@ -0,0 +1,420 @@ +/* + * 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 jdk.jpackage.internal.model.StandardPackageType.MAC_DMG; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.MacPackagingPipeline.MacBuildApplicationTaskID; +import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; +import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.mock.CommandActionSpec; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.MockIllegalStateException; +import jdk.jpackage.test.mock.ScriptSpec; +import jdk.jpackage.test.mock.ScriptSpecInDir; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MacDmgPackagerTest { + + /** + * Exercise branches in {@link MacDmgPackager#buildDMG()}. + */ + @ParameterizedTest + @MethodSource + public void test(DmgScript scriptSpec, @TempDir Path workDir) { + scriptSpec.run(workDir); + } + + private static List test() { + var data = new ArrayList(); + + var succeed = CommandActionSpecs.build().exit().create(); + var fail = CommandActionSpecs.build().exit(1).create(); + + // Test create + for (var createFullSucceed : List.of(true, false)) { + var dmgScript = new DmgScript(); + + var scriptBuilder = ScriptSpec.build(); + + if (createFullSucceed) { + // `hdiutil create -srcfolder` succeeds + scriptBuilder.add(new CommandMockSpec("hdiutil", "hdiutil-create", dmgScript.hdiutilCreate().exit().create())); + } else { + // `hdiutil create -srcfolder` fails + scriptBuilder.add(new CommandMockSpec("hdiutil", "hdiutil-create", fail)); + scriptBuilder.add(new CommandMockSpec("hdiutil", "hdiutil-create-empty", dmgScript.hdiutilCreateEmpty().exit().create())); + } + + scriptBuilder + // `hdiutil attach` succeeds + .add(new CommandMockSpec("hdiutil", "hdiutil-attach", succeed)) + // `osascript` succeeds + .add(new CommandMockSpec("osascript", succeed)) + // `hdiutil detach` succeeds + .add(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach().exit().create())) + // `hdiutil convert` succeeds + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + + data.add(dmgScript.scriptSpec(scriptBuilder.create())); + } + + // Test detach + for (var detachResult : DetachResult.values()) { + var dmgScript = new DmgScript(); + + var scriptBuilder = ScriptSpec.build() + .add(new CommandMockSpec("hdiutil", "hdiutil-create", dmgScript.hdiutilCreate().exit().create())) + .add(new CommandMockSpec("hdiutil", "hdiutil-attach", succeed)) + .add(new CommandMockSpec("osascript", succeed)); + + switch (detachResult) { + case ALL_FAIL -> { + dmgScript.expect(UnexpectedResultException.class); + scriptBuilder.build(new CommandMockSpec("hdiutil", "hdiutil-detach", fail)).repeat(9).add(); + } + case LAST_SUCCEED -> { + scriptBuilder + .build(new CommandMockSpec("hdiutil", "hdiutil-detach", fail)).repeat(8).add() + .add(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach().exit().create())) + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + } + case FIRST_SUCCEED_WITH_EXIT_1 -> { + scriptBuilder + .build(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach().exit(1).create())) + .detailedDescription().add() + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + } + case FIRST_SUCCEED_MOUNT_POINT_REMAINS -> { + scriptBuilder + .build(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach(false).exit().create())) + .detailedDescription().add() + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + } + } + + data.add(dmgScript.scriptSpec(scriptBuilder.create())); + } + + return data; + } + + private enum DetachResult { + ALL_FAIL, + LAST_SUCCEED, + // The first `hdiutil detach` attempt exits with exit code "1" but deletes the mounted directory. + FIRST_SUCCEED_WITH_EXIT_1, + // The first `hdiutil detach` attempt exits with exit code "0" and the mounted directory stays undeleted. + FIRST_SUCCEED_MOUNT_POINT_REMAINS, + ; + } + + private static MacDmgSystemEnvironment createSysEnv(ScriptSpec scriptSpec) { + return new MacDmgSystemEnvironment( + Path.of("hdiutil"), + Path.of("osascript"), + Stream.of("SetFile").map(Path::of).filter(scriptSpec.commandNames()::contains).findFirst() + ); + } + + private static RuntimeBuilder createRuntimeBuilder() { + return new RuntimeBuilder() { + @Override + public void create(AppImageLayout appImageLayout) { + throw new UnsupportedOperationException(); + } + }; + } + + private static void runPackagingMock(Path workDir, MacDmgSystemEnvironment sysEnv) { + + var app = new ApplicationBuilder() + .appImageLayout(MacPackagingPipeline.APPLICATION_LAYOUT) + .runtimeBuilder(createRuntimeBuilder()) + .name("foo") + .create(); + + var macApp = new MacApplicationBuilder(app).create(); + + var macDmgPkg = new MacDmgPackageBuilder(new MacPackageBuilder(new PackageBuilder(macApp, MAC_DMG))).create(); + + var buildEnv = new BuildEnvBuilder(workDir.resolve("build-root")).appImageDirFor(macDmgPkg).create(); + + var packager = new MacDmgPackager(buildEnv, macDmgPkg, workDir, sysEnv); + + var pipelineBuilder = MacPackagingPipeline.build(Optional.of(packager.pkg())); + packager.accept(pipelineBuilder); + + // Disable actions of tasks filling an application image. + Stream.concat( + Stream.of(BuildApplicationTaskID.values()), + Stream.of(MacBuildApplicationTaskID.values()) + ).forEach(taskId -> { + pipelineBuilder.task(taskId).noaction().add(); + }); + + var contentMock = new ContentMock(); + + // Fill application image with content mock. + pipelineBuilder.task(BuildApplicationTaskID.CONTENT).applicationAction(env -> { + contentMock.create(env.resolvedLayout().contentDirectory()); + }).add(); + + pipelineBuilder.create().execute(buildEnv, packager.pkg(), packager.outputDir()); + + var outputDmg = packager.outputDir().resolve(packager.pkg().packageFileNameWithSuffix()); + + contentMock.verifyStoredInFile(outputDmg); + } + + private static final class DmgScript extends ScriptSpecInDir { + + @Override + public String toString() { + var sb = new StringBuilder(); + sb.append(super.toString()); + Optional.ofNullable(expectedErrorType).ifPresent(type -> { + sb.append("; ").append(type.getCanonicalName()); + }); + return sb.toString(); + } + + @Override + public DmgScript scriptSpec(ScriptSpec v) { + super.scriptSpec(v); + return this; + } + + DmgScript expect(Class v) { + expectedErrorType = v; + return this; + } + + void run(Path workDir) { + + var script = dir(Objects.requireNonNull(workDir)).create(); + + ExecutorFactory executorFactory = MockUtils.buildJPackage() + .script(script).listener(System.out::println).createExecutorFactory(); + + var objectFactory = ObjectFactory.build() + .executorFactory(executorFactory) + .retryExecutorFactory(new RetryExecutorFactory() { + @Override + public RetryExecutor retryExecutor(Class exceptionType) { + return RetryExecutorFactory.DEFAULT.retryExecutor(exceptionType).setSleepFunction(_ -> { + // Don't "sleep" to make the test run faster. + }); + } + }) + .create(); + + Globals.main(() -> { + Globals.instance().objectFactory(objectFactory); + if (expectedErrorType == null) { + runPackagingMock(workDir, createSysEnv(scriptSpec())); + } else { + var ex = assertThrows(Exception.class, () -> { + runPackagingMock(workDir, createSysEnv(scriptSpec())); + }); + var cause = ExceptionBox.unbox(ex); + assertEquals(expectedErrorType, cause.getClass()); + } + return 0; + }); + + assertEquals(List.of(), script.incompleteMocks()); + } + + CommandActionSpecs.Builder hdiutilCreate(boolean empty) { + CommandActionSpec action = CommandActionSpec.create("create", context -> { + var dstDmg = Path.of(context.optionValue("-ov")); + assertTrue(isPathInDir(dstDmg)); + + var volumeName = context.optionValue("-volname"); + + if (empty) { + createDmg(new CreateDmgResult(dstDmg, volumeName, Optional.empty())); + Files.createFile(dstDmg); + } else { + var srcDir = Path.of(context.optionValue("-srcfolder")); + assertTrue(isPathInDir(srcDir)); + + createDmg(new CreateDmgResult(dstDmg, volumeName, Optional.of(srcDir))); + + try (var walk = Files.walk(srcDir)) { + var paths = walk.map(srcDir::relativize).map(Path::toString).toList(); + Files.write(dstDmg, paths); + } + } + }); + return CommandActionSpecs.build().action(action); + } + + CommandActionSpecs.Builder hdiutilCreate() { + return hdiutilCreate(false); + } + + CommandActionSpecs.Builder hdiutilCreateEmpty() { + return hdiutilCreate(true); + } + + CommandActionSpecs.Builder hdiutilDetach() { + return hdiutilDetach(true); + } + + CommandActionSpecs.Builder hdiutilDetach(boolean deleteMountPoint) { + var sb = new StringBuilder(); + sb.append("detach"); + if (!deleteMountPoint) { + sb.append("(rm-mount-point)"); + } + CommandActionSpec action = CommandActionSpec.create(sb.toString(), context -> { + var mountPoint = Path.of(context.args().getLast()); + assertTrue(isPathInDir(mountPoint)); + + try (var walk = Files.walk(mountPoint)) { + var dstDmg = dmg().dmg(); + var paths = Stream.concat( + walk.map(mountPoint::relativize), + Files.readAllLines(dstDmg).stream().filter(Predicate.not(String::isEmpty)).map(Path::of) + ).sorted().map(Path::toString).toList(); + Files.write(dstDmg, paths); + } + + if (deleteMountPoint) { + FileUtils.deleteRecursive(mountPoint); + } + }); + return CommandActionSpecs.build().action(action); + } + + CommandActionSpecs.Builder hdiutilConvert() { + CommandActionSpec action = CommandActionSpec.create("convert", context -> { + var srcDmg = Path.of(context.args().get(1)); + assertTrue(isPathInDir(srcDmg)); + + var dstDmg = Path.of(context.args().getLast()); + assertTrue(isPathInDir(dstDmg)); + + Files.copy(srcDmg, dstDmg); + }); + return CommandActionSpecs.build().action(action); + } + + private void createDmg(CreateDmgResult v) { + if (dmg != null) { + throw new MockIllegalStateException("The DMG already set"); + } else { + dmg = Objects.requireNonNull(v); + } + } + + private CreateDmgResult dmg() { + if (dmg == null) { + throw new MockIllegalStateException("The DMG not set"); + } else { + return dmg; + } + } + + private record CreateDmgResult(Path dmg, String volumeName, Optional srcFolder) { + CreateDmgResult { + Objects.requireNonNull(dmg); + Objects.requireNonNull(volumeName); + Objects.requireNonNull(srcFolder); + } + } + + private CreateDmgResult dmg; + private Class expectedErrorType; + } + + private static final class ContentMock { + + void create(Path dir) throws IOException { + Files.createDirectories(dir.resolve("foo/bar")); + Files.writeString(dir.resolve("foo/bar/buz"), "Hello!"); + if (!OperatingSystem.isWindows()) { + Files.createSymbolicLink(dir.resolve("symlink"), Path.of("foo")); + } + } + + void verifyStoredInFile(Path file) { + try { + var expectedPaths = Stream.of( + Stream.of(Path.of("")), + DMG_ICON_FILES.stream(), + Stream.of( + Stream.of("foo/bar/buz"), + Stream.of("symlink").filter(_ -> { + return !OperatingSystem.isWindows(); + }) + ).flatMap(x -> x).map(Path::of).map(Path.of("foo.app/Contents")::resolve) + ).flatMap(x -> x).mapMulti(EXPAND_PATH).sorted().distinct().toList(); + var actualPaths = Files.readAllLines(file).stream().map(Path::of).toList(); + assertEquals(expectedPaths, actualPaths); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + private final static BiConsumer> EXPAND_PATH = (path, sink) -> { + do { + sink.accept(path); + path = path.getParent(); + } while (path != null); + }; + + private final static List DMG_ICON_FILES = Stream.of( + ".VolumeIcon.icns", + ".background/background.tiff" + ).map(Path::of).collect(Collectors.toUnmodifiableList()); +} diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java new file mode 100644 index 00000000000..de2b07e86a6 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.Script; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MacDmgSystemEnvironmentTest { + + @ParameterizedTest + @MethodSource + void test_findSetFileUtility(FindSetFileUtilityTestSpec test) { + test.run(); + } + + private static List test_findSetFileUtility() { + var data = new ArrayList(); + + var succeed = CommandActionSpecs.build().exit().create(); + + for (var failureCause : List.of(CommandMockExit.EXIT_1, CommandMockExit.THROW_MOCK_IO_EXCEPTION)) { + + var fail = CommandActionSpecs.build().exit(failureCause).create(); + + for (var i = 0; i != MacDmgSystemEnvironment.SETFILE_KNOWN_PATHS.size(); i++) { + + var expected = MacDmgSystemEnvironment.SETFILE_KNOWN_PATHS.get(i); + + var mocks = new ArrayList(); + + MacDmgSystemEnvironment.SETFILE_KNOWN_PATHS.subList(0, i).stream().map(failureSetFilePath -> { + return new CommandMockSpec(failureSetFilePath, fail); + }).forEach(mocks::add); + + mocks.add(new CommandMockSpec(expected, succeed)); + + data.add(new FindSetFileUtilityTestSpec(Optional.of(expected), mocks)); + } + + var lastMocks = data.getLast().mockSpecs(); + var lastSucceedMock = lastMocks.getLast(); + var lastFailMock = new CommandMockSpec(lastSucceedMock.name(), lastSucceedMock.mockName(), fail); + + var mocks = new ArrayList<>(lastMocks); + mocks.set(mocks.size() - 1, lastFailMock); + + for (var xcrunOutout : List., Boolean>>of( + // Use the path to the command of the current process + // as an output mock for the /usr/bin/xcrun command. + // MacDmgSystemEnvironment.findSetFileUtility() reads the command output + // and checks whether it is an executable file, + // so the hardcoded value is not an option for the output mock. + Map.entry(Optional.of(ProcessHandle.current().info().command().orElseThrow()), true), + // "/usr/bin/xcrun" outputs a path to non-executable file. + Map.entry(Optional.of("/dev/null"), false), + // "/usr/bin/xcrun" outputs '\0' making subsequent Path.of("\0") fail. + Map.entry(Optional.of("\0"), false), + // "/usr/bin/xcrun" doesn't output anything. + Map.entry(Optional.empty(), false) + )) { + + + mocks.add(new CommandMockSpec("/usr/bin/xcrun", CommandActionSpecs.build().mutate(builder -> { + xcrunOutout.getKey().ifPresent(builder::stdout); + }).exit(CommandMockExit.SUCCEED).create())); + + Optional expected; + if (xcrunOutout.getValue()) { + expected = xcrunOutout.getKey(); + } else { + expected = Optional.empty(); + } + + data.add(new FindSetFileUtilityTestSpec(expected.map(Path::of), List.copyOf(mocks))); + + mocks.removeLast(); + } + + // The last test case: "/usr/bin/xcrun" fails + mocks.add(new CommandMockSpec("/usr/bin/xcrun", fail)); + data.add(new FindSetFileUtilityTestSpec(Optional.empty(), mocks)); + } + + return data; + } + + record FindSetFileUtilityTestSpec(Optional expected, List mockSpecs) { + + FindSetFileUtilityTestSpec { + Objects.requireNonNull(expected); + Objects.requireNonNull(mockSpecs); + } + + @Override + public String toString() { + var tokens = new ArrayList(); + expected.ifPresent(v -> { + tokens.add(String.format("expect=%s", v)); + }); + tokens.add(mockSpecs.toString()); + return tokens.stream().collect(Collectors.joining(", ")); + } + + void run() { + + var script = Script.build().mutate(builder -> { + mockSpecs.forEach(builder::map); + }).createSequence(); + + Globals.main(() -> { + MockUtils.buildJPackage().script(script).applyToGlobals(); + + var actual = MacDmgSystemEnvironment.findSetFileUtility(); + + assertEquals(expected, actual); + assertEquals(List.of(), script.incompleteMocks()); + + return 0; + }); + } + } +} diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java index 1549aaa6cd7..c7fd2bc5f8d 100644 --- a/test/jdk/tools/jpackage/junit/macosx/junit.java +++ b/test/jdk/tools/jpackage/junit/macosx/junit.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 @@ -30,3 +30,25 @@ * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.MacApplicationLayoutTest */ + +/* @test + * @summary Test MacDmgSystemEnvironmentTest + * @requires (os.family == "mac") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgSystemEnvironmentTest + */ + +/* @test + * @summary Test MacDmgPackagerTest + * @requires (os.family == "mac") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacDmgPackagerTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgPackagerTest + */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java index 375c6aa637a..53f4b9b95aa 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_S import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_NAME; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LINUX_LAUNCHER_SHORTCUT; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_APP_STORE; -import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_SIGNED; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.WIN_LAUNCHER_DESKTOP_SHORTCUT; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.WIN_LAUNCHER_MENU_SHORTCUT; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; @@ -514,7 +513,6 @@ public class AppImageFileTest { "Foo", "", "property-x", - "true", "False", "", " Quick brown fox", @@ -546,8 +544,7 @@ public class AppImageFileTest { .addExtra(WIN_LAUNCHER_MENU_SHORTCUT, new LauncherShortcut(LauncherShortcutStartupDirectory.APP_DIR)).commit()).create()); testCases.add(builder.os(OperatingSystem.MACOS).expect(appBuilder.get().commit() - .addExtra(MAC_APP_STORE, false) - .addExtra(MAC_SIGNED, true)).create()); + .addExtra(MAC_APP_STORE, false)).create()); return testCases; } @@ -580,7 +577,6 @@ public class AppImageFileTest { "OverwrittenMain", "Main", "property-x", - "true", "", " foo", " service-launcher description", diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java index 35ab1fbec5e..709f0f8413b 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.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 @@ -22,15 +22,42 @@ */ package jdk.jpackage.internal; + +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.mock.CommandMock.ioerror; +import static jdk.jpackage.test.mock.CommandMock.succeed; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.StandardBundlingOperation; +import jdk.jpackage.internal.model.AppImageBundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.test.Annotations; +import jdk.jpackage.test.HelloApp; +import jdk.jpackage.test.JUnitAdapter; +import jdk.jpackage.test.JavaAppDesc; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMock; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.Script; import org.junit.jupiter.api.Test; -public class DefaultBundlingEnvironmentTest { +public class DefaultBundlingEnvironmentTest extends JUnitAdapter { @Test void testDefaultBundlingOperation() { @@ -55,4 +82,200 @@ public class DefaultBundlingEnvironmentTest { assertEquals(descriptor, env.defaultOperation().orElseThrow()); assertEquals(1, executed[0]); } + + /** + * Tests that commands executed to initialize the system environment are + * executed only once. + * @throws IOException + */ + @Annotations.Test + @Annotations.ParameterSupplier + public void testInitializedOnce(StandardBundlingOperation op) throws IOException { + + List> executedCommands = Collections.synchronizedList(new ArrayList<>()); + + var script = createMockScript(op); + + ToolProvider jpackage = MockUtils.buildJPackage() + .os(op.os()) + .script(script) + .listener(executedCommands::add).create(); + + var inputDir = TKit.createTempDirectory("input"); + var appDesc = JavaAppDesc.parse(null); + HelloApp.createBundle(appDesc, inputDir); + + // + // The command line should fail as the main class name is not specified and it is not set in the main jar. + // + // Run native packaging twice. + // It can execute commands required to configure the system environment in the first iteration. + // It must not execute a single command in the second iteration. + // + // Run app image packaging once. + // It must not execute a single command because app image packaging should not require native commands (Unless + // it is macOS where it will sign the app image with an ad hoc signature + // using the codesign tool. But: #1 - it is not a variable part of the system environment; + // #2 - jpackage should bail out earlier). + // + + final var type = op.bundleTypeValue(); + final int iterationCount; + if (op.bundleType() instanceof AppImageBundleType) { + iterationCount = 1; + } else { + iterationCount = 2; + } + + for (var i = 0; i != iterationCount; i++) { + var result = new Executor().toolProvider(jpackage).saveOutput().args( + "--type=" + type, + "--input", inputDir.toString(), + "--main-jar", appDesc.jarFileName()).execute(); + + assertEquals(1, result.getExitCode()); + + // Assert it bailed out with the expected error. + assertEquals(List.of( + I18N.format("message.error-header", I18N.format("error.no-main-class-with-main-jar", appDesc.jarFileName())), + I18N.format("message.advice-header", I18N.format("error.no-main-class-with-main-jar.advice", appDesc.jarFileName())) + ), result.stderr()); + + TKit.trace("The list of executed commands:"); + executedCommands.forEach(cmdline -> { + TKit.trace(" " + cmdline); + }); + TKit.trace("Done"); + + if (i == 0) { + executedCommands.clear(); + } + } + + assertEquals(List.of(), executedCommands); + assertEquals(List.of(), script.incompleteMocks()); + } + + public static List testInitializedOnce() { + return StandardBundlingOperation.ofPlatform(OperatingSystem.current()) + .filter(StandardBundlingOperation::isCreateBundle).map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static Script createMockScript(StandardBundlingOperation op) { + + if (op.bundleType() instanceof AppImageBundleType) { + return Script.build().createSequence(); + } + + switch (op.os()) { + case WINDOWS -> { + return createWinMockScript(); + } + case LINUX -> { + return createLinuxMockScript(op.packageType()); + } + case MACOS -> { + return createMacMockScript(); + } + default -> { + throw new AssertionError(); + } + } + } + + private static Script createWinMockScript() { + + // Make "candle.exe" and "light.exe" always fail. + var candle = ioerror("candle-mock"); + var light = ioerror("light-mock"); + + // Make the "wix.exe" functional. + var wix = CommandActionSpecs.build() + .stdout("5.0.2+aa65968c") + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name("wix-mock").create(); + + var script = Script.build() + .map(Script.cmdlineStartsWith("candle.exe"), candle) + .map(Script.cmdlineStartsWith("light.exe"), light) + .map(Script.cmdlineStartsWith("wix.exe"), wix) + .createLoop(); + + return script; + } + + private static Script createMacMockScript() { + + @SuppressWarnings("unchecked") + var setfilePaths = (List)toSupplier(() -> { + return Class.forName(String.join(".", + DefaultBundlingEnvironmentTest.class.getPackageName(), + "MacDmgSystemEnvironment" + )).getDeclaredField("SETFILE_KNOWN_PATHS").get(null); + }).get(); + + var script = Script.build(); + + for (var setfilePath: setfilePaths) { + script.map(Script.cmdlineStartsWith(setfilePath), ioerror(setfilePath.toString() + "-mock")); + } + + script.map(Script.cmdlineStartsWith("/usr/bin/xcrun"), succeed("/usr/bin/xcrun-mock")); + + return script.createLoop(); + } + + private static Script createLinuxMockScript(PackageType pkgType) { + + final Map mocks = new HashMap<>(); + + var script = Script.build(); + + final Set debCommandNames = Set.of("dpkg", "dpkg-deb", "fakeroot"); + final Set rpmCommandNames = Set.of("rpm", "rpmbuild"); + + final Set succeedCommandNames; + switch (pkgType) { + case StandardPackageType.LINUX_DEB -> { + succeedCommandNames = debCommandNames; + // Simulate "dpkg --print-architecture". + var dpkg = CommandActionSpecs.build() + .stdout("foo-arch") + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name("dpkg-mock").create(); + mocks.put("dpkg", dpkg); + } + case StandardPackageType.LINUX_RPM -> { + succeedCommandNames = rpmCommandNames; + // Simulate "rpmbuild --version" prints the minimal acceptable version. + var rpmbuild = CommandActionSpecs.build() + .stdout("RPM version 4.10") + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name("rpmbuild-mock").create(); + mocks.put("rpmbuild", rpmbuild); + } + default -> { + throw new IllegalArgumentException(); + } + } + + script.map(Script.cmdlineStartsWith("ldd"), succeed("ldd-mock")); + + for (var commandName : succeedCommandNames) { + if (!mocks.containsKey(commandName)) { + mocks.put(commandName, succeed(commandName + "-mock")); + } + } + + Stream.of(debCommandNames, rpmCommandNames).flatMap(Set::stream).forEach(commandName -> { + var mock = Optional.ofNullable(mocks.get(commandName)).orElseGet(() -> { + return ioerror(commandName + "-mock"); + }); + script.map(Script.cmdlineStartsWith(commandName), mock); + }); + + return script.createLoop(); + } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java new file mode 100644 index 00000000000..551ef15e991 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java @@ -0,0 +1,165 @@ +/* + * 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.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CompletableCommandMock; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class ExecutorTest { + + @ParameterizedTest + @MethodSource + public void test_retryOnKnownErrorMessage(RetryOnKnownErrorMessageTestSpec test) { + test.run(); + } + + private static Stream test_retryOnKnownErrorMessage() { + var data = new ArrayList(); + + final var subject = "French fries"; + + Supplier build = () -> { + return RetryOnKnownErrorMessageTestSpec.build().subject(subject); + }; + + for (var exit : Stream.of(CommandMockExit.values()).filter(CommandMockExit::exitNormally).toList()) { + // These should succeed as there is no "French fries" in stderr. + Stream.of( + build.get().mock(CommandActionSpecs.build().stderr("Coleslaw").exit(exit)), + build.get().mock(CommandActionSpecs.build().stdout(subject).exit(exit)), + build.get() + // Fail in the first attempt (triggering text in the stderr) + .mock(CommandActionSpecs.build().stderr(subject).exit()) + // Fail in the second attempt (same reason) + .repeatLastMoc() + // Pass in the next attempt (no triggering text in the stderr) + .mock(CommandActionSpecs.build().stderr("Coleslaw").exit(exit)), + build.get() + // Fail in the first attempt (triggering text in the stderr) + .mock(CommandActionSpecs.build().stderr(subject)) + // Fail in the second attempt (error running the command) + .mock(CommandActionSpecs.build().exit(CommandMockExit.THROW_MOCK_IO_EXCEPTION)) + // Pass in the next attempt (no triggering text in the stderr) + .mock(CommandActionSpecs.build().exit(exit)) + ).map(RetryOnKnownErrorMessageTestSpec.Builder::success).forEach(data::add); + } + + // These should fail as there is "French fries" in stderr. + data.addAll(List.of( + // Try once and fail. + build.get().mock(CommandActionSpecs.build().stderr(subject).exit()), + // Try twice and fail. + build.get().mock(CommandActionSpecs.build().stderr(subject).exit()).repeatLastMoc() + )); + + return data.stream().map(RetryOnKnownErrorMessageTestSpec.Builder::create); + } + + record RetryOnKnownErrorMessageTestSpec(List mockSpecs, String subject, boolean success) { + + RetryOnKnownErrorMessageTestSpec { + Objects.requireNonNull(mockSpecs); + Objects.requireNonNull(subject); + + if (mockSpecs.isEmpty()) { + throw new IllegalArgumentException(); + } + } + + void run() { + var mock = mockSpecs.stream() + .reduce(CommandActionSpecs::andThen) + .orElseThrow().toCommandMockBuilder() + // Ensure attempts to run the command more times than expected will fail. + .noRepeats().create(); + + var retry = new Executor().toolProvider(mock).retryOnKnownErrorMessage(subject) + .setAttemptTimeout(null) + .setMaxAttemptsCount(mockSpecs.size()); + + if (success) { + assertDoesNotThrow(retry::execute); + } else { + assertThrowsExactly(UnexpectedResultException.class, retry::execute); + } + + assertTrue(((CompletableCommandMock)mock).completed()); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + RetryOnKnownErrorMessageTestSpec create() { + return new RetryOnKnownErrorMessageTestSpec(mockSpecs, subject, success); + } + + public Builder mock(CommandActionSpecs v) { + mockSpecs.add(Objects.requireNonNull(v)); + return this; + } + + public Builder mock(CommandActionSpecs.Builder v) { + return mock(v.create()); + } + + public Builder repeatLastMoc() { + return mock(mockSpecs.getLast()); + } + + public Builder subject(String v) { + subject = v; + return this; + } + + public Builder success(boolean v) { + success = v; + return this; + } + + public Builder success() { + return success(true); + } + + private final List mockSpecs = new ArrayList<>(); + private String subject; + private boolean success; + } + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java new file mode 100644 index 00000000000..e88077a6c9d --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java @@ -0,0 +1,235 @@ +/* + * 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 jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.CliBundlingEnvironment; +import jdk.jpackage.internal.cli.Main; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.ToolProviderCommandMock; +import jdk.jpackage.test.mock.VerbatimCommandMock; + +/** + * Bridges "jdk.jpackage.internal" and "jdk.jpackage.test.mock" packages. + */ +public final class MockUtils { + + private MockUtils() { + } + + public static JPackageToolProviderBuilder buildJPackage() { + return new JPackageToolProviderBuilder(); + } + + public static final class JPackageToolProviderBuilder { + + public ToolProvider create() { + return createJPackageToolProvider(os(), createObjectFactory()); + } + + public Consumer createGlobalsMutator() { + var objectFactory = createObjectFactory(); + return globals -> { + globals.objectFactory(objectFactory); + }; + } + + public void applyToGlobals() { + createGlobalsMutator().accept(Globals.instance()); + } + + ExecutorFactory createExecutorFactory() { + var commandMocksExecutorFactory = Optional.ofNullable(script).map(MockUtils::withCommandMocks).map(mapper -> { + return mapper.apply(ExecutorFactory.DEFAULT); + }).orElse(ExecutorFactory.DEFAULT); + + var recordingExecutorFactory = Optional.ofNullable(listener).map(MockUtils::withCommandListener).map(mapper -> { + return mapper.apply(commandMocksExecutorFactory); + }).orElse(commandMocksExecutorFactory); + + return recordingExecutorFactory; + } + + ObjectFactory createObjectFactory() { + var executorFactory = createExecutorFactory(); + if (executorFactory == ExecutorFactory.DEFAULT) { + return ObjectFactory.DEFAULT; + } else { + return ObjectFactory.build().executorFactory(executorFactory).create(); + } + } + + public JPackageToolProviderBuilder listener(Consumer> v) { + listener = v; + return this; + } + + public JPackageToolProviderBuilder script(Script v) { + script = v; + return this; + } + + public JPackageToolProviderBuilder os(OperatingSystem v) { + os = v; + return this; + } + + private OperatingSystem os() { + return Optional.ofNullable(os).orElseGet(OperatingSystem::current); + } + + private Consumer> listener; + private OperatingSystem os; + private Script script; + } + + public static ToolProvider createJPackageToolProvider(OperatingSystem os, Script script) { + return buildJPackage() + .os(Objects.requireNonNull(os)) + .script(Objects.requireNonNull(script)) + .create(); + } + + public static ToolProvider createJPackageToolProvider(Script script) { + return createJPackageToolProvider(OperatingSystem.current(), script); + } + + private static UnaryOperator withCommandListener(Consumer> listener) { + Objects.requireNonNull(listener); + return executorFactory -> { + Objects.requireNonNull(executorFactory); + return () -> { + var executor = executorFactory.executor(); + + Optional> oldMapper = executor.mapper(); + + UnaryOperator newMapper = exec -> { + listener.accept(exec.commandLine()); + return exec; + }; + + return executor.mapper(oldMapper.map(newMapper::compose).orElse(newMapper)::apply); + }; + }; + } + + private static UnaryOperator withCommandMocks(Script script) { + return executorFactory -> { + Objects.requireNonNull(executorFactory); + return () -> { + var executor = executorFactory.executor(); + + Optional> oldMapper = executor.mapper(); + + UnaryOperator newMapper = exec -> { + var commandLine = exec.commandLine(); + var mock = Objects.requireNonNull(script.map(commandLine)); + switch (mock) { + case VerbatimCommandMock.INSTANCE -> { + // No mock for this command line. + return exec; + } + case ToolProviderCommandMock tp -> { + // Create a copy of the executor with the old mapper to prevent further recursion. + var copy = exec.copy().mapper(oldMapper.orElse(null)); + copy.toolProvider(tp); + copy.args().clear(); + copy.args(commandLine.subList(1, commandLine.size())); + return copy; + } + default -> { + // Unreachable because there are no other cases for this switch. + throw ExceptionBox.reachedUnreachable(); + } + } + }; + + return executor.mapper(oldMapper.map(newMapper::compose).orElse(newMapper)::apply); + }; + }; + } + + public static CliBundlingEnvironment createBundlingEnvironment(OperatingSystem os) { + Objects.requireNonNull(os); + + String bundlingEnvironmentClassName; + switch (os) { + case WINDOWS -> { + bundlingEnvironmentClassName = "WinBundlingEnvironment"; + } + case LINUX -> { + bundlingEnvironmentClassName = "LinuxBundlingEnvironment"; + } + case MACOS -> { + bundlingEnvironmentClassName = "MacBundlingEnvironment"; + } + default -> { + throw new IllegalArgumentException(); + } + } + + return toSupplier(() -> { + var ctor = Class.forName(String.join(".", + DefaultBundlingEnvironment.class.getPackageName(), + bundlingEnvironmentClassName + )).getConstructor(); + return (CliBundlingEnvironment)ctor.newInstance(); + }).get(); + } + + static ToolProvider createJPackageToolProvider(OperatingSystem os, ObjectFactory of) { + Objects.requireNonNull(os); + Objects.requireNonNull(of); + + var impl = new Main.Provider(DefaultBundlingEnvironment.runOnce(() -> { + return createBundlingEnvironment(os); + })); + + return new ToolProvider() { + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + return Globals.main(() -> { + Globals.instance().objectFactory(of); + return impl.run(out, err, args); + }); + } + + @Override + public String name() { + return impl.name(); + } + }; + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java index 721e0802d16..86a6cb075d0 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.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 @@ -633,7 +633,12 @@ public class PackagingPipelineTest { Package create() { return new Package.Stub( app, - new PackageType() {}, + new PackageType() { + @Override + public String label() { + throw new UnsupportedOperationException(); + } + }, "the-package", "My package", "1.0", diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java index 365a8a50b33..79648260274 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.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 @@ -22,6 +22,7 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput.augmentResultWithOutput; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -43,19 +44,31 @@ import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.Globals; +import jdk.jpackage.internal.MockUtils; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.Annotations; +import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JUnitAdapter; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.VerbatimCommandMock; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -public class MainTest extends JUnitAdapter.TestSrcInitializer { +public class MainTest extends JUnitAdapter { @ParameterizedTest @MethodSource @@ -78,6 +91,71 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { assertNotEquals(str, msg); } + /** + * Run app image packaging and simulate jlink failure. + *

    + * The test verifies that jpackage prints an error message with jlink exit code, + * command line, and output. + */ + @Annotations.Test + public void testFailedCommandOutput() { + + var jlinkMockExitCode = 17; + + var jlinkArgs = new ArrayList(); + + var jlinkMock = CommandActionSpecs.build() + .stdout("It").stderr("fell").stdout("apart") + .argsListener(jlinkArgs::addAll) + .exit(jlinkMockExitCode).create().toCommandMockBuilder().name("jlink-mock").create(); + + var script = Script.build() + // Replace jlink with the mock. + .map(Script.cmdlineStartsWith("jlink"), jlinkMock) + // Don't mock other external commands. + .map(_ -> true, VerbatimCommandMock.INSTANCE) + .createLoop(); + + var jpackageExitCode = 1; + + var jpackageToolProviderMock = new ToolProvider() { + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + var globalsMutator = MockUtils.buildJPackage().script(script).createGlobalsMutator(); + return Globals.main(() -> { + globalsMutator.accept(Globals.instance()); + + var result = ExecutionResult.create(args); + + var jlinkMockAttrs = new CommandOutputControl.ToolProviderAttributes(jlinkMock.name(), jlinkArgs); + + result.stdout().forEach(out::println); + result.stderr().forEach(err::println); + + assertEquals(jpackageExitCode, result.exitCode()); + assertEquals(List.of(), result.stdout()); + assertEquals(List.of( + I18N.format("message.error-header", I18N.format("error.command-failed-unexpected-exit-code", + jlinkMockExitCode, jlinkMockAttrs.printableCommandLine())), + I18N.format("message.failed-command-output-header"), + "It", "fell", "apart"), result.stderr()); + + return jpackageExitCode; + }); + } + + @Override + public String name() { + return "jpackage-mock"; + } + }; + + JPackageCommand.helloAppImage() + .ignoreDefaultVerbose(true) + .useToolProvider(jpackageToolProviderMock) + .execute(jpackageExitCode); + } + private static Collection testOutput() { return Stream.of( // Print the tool version @@ -165,6 +243,49 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); } } + + var execAttrs = new CommandOutputControl.ProcessAttributes(Optional.of(12345L), List.of("foo", "--bar")); + for (var makeCause : List.>of( + ex -> ex, + ExceptionBox::toUnchecked + )) { + + for (var expect : List.of( + Map.entry( + augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).execAttrs(execAttrs).create(), + "The quick brown fox\njumps over the lazy dog" + ).unexpected("Kaput!"), + ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE + ), + Map.entry( + new UnexpectedExitCodeException(augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).create(), + "The quick brown fox\njumps" + )), + ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE + ), + Map.entry( + augmentResultWithOutput( + CommandOutputControl.Result.build().create(), + "The quick brown fox\njumps" + ).unexpected("Timed out!"), + ExceptionFormatter.FAILED_COMMAND_TIMEDOUT_MESSAGE + ) + )) { + var cause = makeCause.apply(expect.getKey()); + var expectedOutput = new ArrayList(); + if (verbose) { + expectedOutput.add(ExceptionFormatter.STACK_TRACE); + } + expectedOutput.add(expect.getValue()); + if (!verbose) { + expectedOutput.add(ExceptionFormatter.FAILED_COMMAND_OUTPUT); + } + data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); + } + } + } return data; @@ -350,7 +471,24 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { ex.printStackTrace(pw); } return sink.toString(); - }) + }), + FAILED_COMMAND_OUTPUT(ex -> { + var result = failedCommandResult(ex); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + + var sink = new StringWriter(); + var pw = new PrintWriter(sink); + + pw.println(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(pw::println); + } + + return sink.toString(); + }), + FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE(errorMessage(CommandFailureType.UNEXPECTED_OUTPUT::getMessage)), + FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE(errorMessage(CommandFailureType.UNEXPECTED_EXIT_CODE::getMessage)), + FAILED_COMMAND_TIMEDOUT_MESSAGE(errorMessage(CommandFailureType.TIMEDOUT::getMessage)), ; ExceptionFormatter(Function formatter) { @@ -369,6 +507,42 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { }; } + private static CommandOutputControl.Result failedCommandResult(Exception ex) { + Objects.requireNonNull(ex); + if (ex instanceof UnexpectedResultException urex) { + return urex.getResult(); + } else { + throw new IllegalArgumentException(); + } + } + + private enum CommandFailureType { + UNEXPECTED_OUTPUT, + UNEXPECTED_EXIT_CODE, + TIMEDOUT, + ; + + String getMessage(Exception ex) { + var result = failedCommandResult(ex); + var printableCommandLine = result.execAttrs().printableCommandLine(); + switch (this) { + case TIMEDOUT -> { + return I18N.format("error.command-failed-timed-out", printableCommandLine); + } + case UNEXPECTED_EXIT_CODE -> { + return I18N.format("error.command-failed-unexpected-exit-code", result.getExitCode(), printableCommandLine); + } + case UNEXPECTED_OUTPUT -> { + return I18N.format("error.command-failed-unexpected-output", printableCommandLine); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + } + private final Function formatter; } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 0e7545bd83d..07757211927 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -4,7 +4,6 @@ ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--app-version, 1.]; errors=[mess ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--app-version, 1.b.3]; errors=[message.error-header+[error.version-string-invalid-component, 1.b.3, b.3]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--app-version, ]; errors=[message.error-header+[error.version-string-empty]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --add-modules]; errors=[message.error-header+[error.blocked.option, --add-modules]]) -ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --foo]; errors=[message.error-header+[error.jlink.failed, Error: unknown option: --foo]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --module-path]; errors=[message.error-header+[error.blocked.option, --module-path]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --output]; errors=[message.error-header+[error.blocked.option, --output]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--main-jar, non-existent.jar]; errors=[message.error-header+[error.main-jar-does-not-exist, non-existent.jar]]) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java index a85b5015e73..9826f0e9069 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.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 @@ -200,7 +200,8 @@ public class OptionsValidationFailTest { Stream.of("--jpt-run=ErrorTest") ).flatMap(x -> x).toArray(String[]::new)).map(dynamicTest -> { return DynamicTest.dynamicTest(dynamicTest.getDisplayName(), () -> { - JPackageCommand.withToolProvider(jpackageToolProviderMock, () -> { + TKit.withNewState(() -> { + JPackageCommand.useToolProviderByDefault(jpackageToolProviderMock); try { dynamicTest.getExecutable().execute(); } catch (Throwable t) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java index b786f484cb4..0b70f4151cc 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.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 @@ -53,6 +53,8 @@ import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.cli.JOptSimpleOptionsBuilder.ConvertedOptionsBuilder; import jdk.jpackage.internal.cli.JOptSimpleOptionsBuilder.OptionsBuilder; import jdk.jpackage.internal.cli.StandardOption.LauncherProperty; +import jdk.jpackage.internal.model.AppImageBundleType; +import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; @@ -175,16 +177,21 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { assertEquals(DirectoryNotEmptyException.class, ex.getCause().getClass()); } + @ParameterizedTest + @EnumSource(names = {"WINDOWS", "LINUX", "MACOS"}) + public void test_TYPE_valid(OperatingSystem appImageOS) { + + var spec = new StandardOptionContext(appImageOS).mapOptionSpec(StandardOption.TYPE.getSpec()); + + test_TYPE_valid(spec, appImageOS); + } + @Test public void test_TYPE_valid() { var spec = StandardOption.TYPE.getSpec(); - Stream.of(StandardBundlingOperation.values()).forEach(bundlingOperation -> { - var pkgTypeStr = bundlingOperation.packageTypeValue(); - var pkgType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(pkgTypeStr)).orElseThrow(); - assertSame(bundlingOperation.packageType(), pkgType); - }); + test_TYPE_valid(spec, OperatingSystem.current()); } @ParameterizedTest @@ -336,14 +343,50 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { assertEquals(expectedOptionTable, optionTable); } + private void test_TYPE_valid(OptionSpec spec, OperatingSystem appImageOS) { + Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> { + // Skip app image bundle type if it is from another platform. + return !(bundlingOperation.bundleType() instanceof AppImageBundleType) + || (bundlingOperation.os() == appImageOS); + }).forEach(bundlingOperation -> { + var bundleTypeStr = bundlingOperation.bundleTypeValue(); + var bundleType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow(); + assertSame(bundlingOperation.bundleType(), bundleType); + }); + } + private static Collection test_ARGUMENTS() { return List.of( Arguments.of("abc", List.of("abc")), Arguments.of("a b c", List.of("a", "b", "c")), Arguments.of("a=10 -Dorg.acme.name='John Smith' c=\\\"foo\\\"", List.of("a=10", "-Dorg.acme.name=John Smith", "c=\"foo\"")), + Arguments.of(" foo \"a b c\" v=' John Smith ' 'H e ll o' ", List.of("foo", "a b c", "v= John Smith ", "H e ll o")), Arguments.of("\"\"", List.of("")), Arguments.of(" ", List.of()), - Arguments.of("", List.of()) + Arguments.of(" ", List.of()), + Arguments.of(" foo ", List.of("foo")), + Arguments.of("", List.of()), + Arguments.of("'fo\"o'\\ buzz \"b a r\"", List.of("fo\"o\\ buzz", "b a r")), + Arguments.of("a\\ 'b\"c'\\ d", List.of("a\\ b\"c\\ d")), + Arguments.of("\"a 'bc' d\"", List.of("a 'bc' d")), + Arguments.of("\'a 'bc' d\'", List.of("a bc d")), + Arguments.of("\"a \\'bc\\' d\"", List.of("a 'bc' d")), + Arguments.of("\'a \\'bc\\' d\'", List.of("a 'bc' d")), + Arguments.of("'a b c' 'd e f'", List.of("a b c", "d e f")), + Arguments.of("'a b c' \"'d e f' h", List.of("a b c", "'d e f' h")), + Arguments.of("'a b c' \"'d e f' \t ", List.of("a b c", "'d e f'")), + Arguments.of(" a='' '' \t '\\'\\'' \"\" \"\\\"\\\"\" ", List.of("a=", "", "\'\'", "", "\"\"")), + Arguments.of("' \'foo '", List.of(" foo", "")), + Arguments.of("' \'foo ' bar", List.of(" foo", " bar")), + Arguments.of("' \'foo\\ '", List.of(" foo\\ ")), + Arguments.of("'fo\"o buzz \"b a r\"", List.of("fo\"o buzz \"b a r\"")), + Arguments.of("'", List.of("")), + Arguments.of("' f g ", List.of(" f g")), + Arguments.of("' f g", List.of(" f g")), + Arguments.of("'\\'", List.of("'")), + Arguments.of("'\\' ", List.of("'")), + Arguments.of("'\\' a ", List.of("' a")), + Arguments.of("\"" + "\\\"".repeat(10000) + "A", List.of("\"".repeat(10000) + "A")) ); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.java index d3e9ecb09e9..2acf45b663d 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.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 @@ -34,6 +34,8 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.cli.Validator.ParsedValue; +import jdk.jpackage.internal.cli.Validator.ValidatorException; import jdk.jpackage.test.JUnitUtils; final class TestUtils { @@ -152,6 +154,31 @@ final class TestUtils { } + static final class RecordingValidator implements Validator { + + RecordingValidator(Validator validator) { + this.validator = Objects.requireNonNull(validator); + } + + @Override + public List validate(OptionName optionName, ParsedValue optionValue) { + counter++; + return validator.validate(optionName, optionValue); + } + + int counter() { + return counter; + } + + void resetCounter() { + counter = 0; + } + + private final Validator validator; + private int counter; + } + + private record RecordingExceptionFactory(OptionValueExceptionFactory factory, Consumer sink) implements OptionValueExceptionFactory { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.java index d4c48df6a0c..d8d69027f77 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.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 @@ -32,10 +32,11 @@ import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Stream; import jdk.jpackage.internal.cli.TestUtils.TestException; +import jdk.jpackage.internal.cli.TestUtils.RecordingValidator; import jdk.jpackage.internal.cli.Validator.ParsedValue; import jdk.jpackage.internal.cli.Validator.ValidatingConsumerException; import jdk.jpackage.internal.cli.Validator.ValidatorException; @@ -187,46 +188,97 @@ public class ValidatorTest { } @Test - public void test_andThen() { - - Function> createFailingValidator = exceptionMessage -> { - Objects.requireNonNull(exceptionMessage); - var exceptionFactory = OptionValueExceptionFactory.build().ctor(TestException::new).messageFormatter((_, _) -> { - return exceptionMessage; - }).create(); - - return Validator.build() - .predicate(_ -> false) - .formatString("") - .exceptionFactory(exceptionFactory).create(); - }; + public void test_and() { Function, List> validate = validator -> { return validator.validate(OptionName.of("a"), ParsedValue.create("str", StringToken.of("str"))); }; - var pass = Validator.build().predicate(_ -> true).create(); + var pass = new RecordingValidator<>(Validator.build().predicate(_ -> true).create()); - var foo = createFailingValidator.apply("foo"); - var bar = createFailingValidator.apply("bar"); - var buz = createFailingValidator.apply("buz"); + var foo = failingValidator("foo"); + var bar = failingValidator("bar"); + var buz = failingValidator("buz"); assertExceptionListEquals(List.of( new TestException("foo"), new TestException("bar"), new TestException("buz") - ), validate.apply(foo.andThen(bar).andThen(pass).andThen(buz))); + ), validate.apply(foo.and(bar).and(pass).and(buz))); + assertEquals(1, pass.counter()); + pass.resetCounter(); assertExceptionListEquals(List.of( new TestException("bar"), new TestException("buz"), new TestException("foo") - ), validate.apply(pass.andThen(bar).andThen(buz).andThen(foo))); + ), validate.apply(pass.and(bar).and(buz).and(foo))); + assertEquals(1, pass.counter()); assertExceptionListEquals(List.of( new TestException("foo"), new TestException("foo") - ), validate.apply(foo.andThen(foo))); + ), validate.apply(foo.and(foo))); + + pass.resetCounter(); + assertExceptionListEquals(List.of( + ), validate.apply(pass.and(pass))); + assertEquals(2, pass.counter()); + } + + @Test + public void test_or() { + + Function, List> validate = validator -> { + return validator.validate(OptionName.of("a"), ParsedValue.create("str", StringToken.of("str"))); + }; + + var pass = new RecordingValidator<>(Validator.build().predicate(_ -> true).create()); + + var foo = new RecordingValidator<>(failingValidator("foo")); + var bar = new RecordingValidator<>(failingValidator("bar")); + var buz = new RecordingValidator<>(failingValidator("buz")); + + Runnable resetCounters = () -> { + Stream.of(pass, foo, bar, buz).forEach(RecordingValidator::resetCounter); + }; + + assertExceptionListEquals(List.of( + new TestException("foo"), + new TestException("bar"), + new TestException("buz") + ), validate.apply(foo.or(bar).or(buz))); + assertEquals(1, foo.counter()); + assertEquals(1, bar.counter()); + assertEquals(1, buz.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + ), validate.apply(foo.or(bar).or(pass).or(buz))); + assertEquals(1, foo.counter()); + assertEquals(1, bar.counter()); + assertEquals(1, pass.counter()); + assertEquals(0, buz.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + ), validate.apply(pass.or(bar).or(buz).or(foo))); + assertEquals(1, pass.counter()); + assertEquals(0, bar.counter()); + assertEquals(0, buz.counter()); + assertEquals(0, foo.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + new TestException("foo"), + new TestException("foo") + ), validate.apply(foo.or(foo))); + assertEquals(2, foo.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + ), validate.apply(pass.or(pass))); + assertEquals(1, pass.counter()); } @ParameterizedTest @@ -269,6 +321,17 @@ public class ValidatorTest { return data; } + private static Validator failingValidator(String exceptionMessage) { + var exceptionFactory = OptionValueExceptionFactory.build().ctor(TestException::new).messageFormatter((_, _) -> { + return exceptionMessage; + }).create(); + + return Validator.build() + .predicate(_ -> false) + .formatString("") + .exceptionFactory(exceptionFactory).create(); + } + static final class FooException extends Exception { 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 new file mode 100644 index 00000000000..d71cf7c4d41 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java @@ -0,0 +1,1867 @@ +/* + * 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.util; + +import static java.util.stream.Collectors.joining; +import static jdk.jpackage.internal.util.CommandOutputControlTestUtils.isInterleave; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.JUnitUtils.assertArrayEquals; +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.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.HexFormat; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.function.ExceptionBox; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class CommandOutputControlTest { + + @DisabledIf("cherryPickSavedOutputTestCases") + @ParameterizedTest + @MethodSource + public void testSavedOutput(OutputTestSpec spec) { + spec.test(); + } + + /** + * Runs cherry-picked {@link OutputTestSpec} test cases. + *

    + * This test method is mutual exclusive with + * {@link #testSavedOutput(OutputTestSpec)} and is aimed for debugging + * {@code OutputTestSpec} test cases. + *

    + * It is disabled by default. To enable it, manually edit {@link #testSomeSavedOutput()}. + * + * @see #testSomeSavedOutput() + * + * @param spec the test case + */ + @EnabledIf("cherryPickSavedOutputTestCases") + @ParameterizedTest + @MethodSource + public void testSomeSavedOutput(OutputTestSpec spec) { + System.out.println(spec); + spec.test(); + } + + @ParameterizedTest + @MethodSource + public void testDumpStreams(OutputTestSpec spec) { + spec.test(); + } + + @ParameterizedTest + @MethodSource + public void testCharset(CharsetTestSpec spec) throws IOException, InterruptedException { + spec.test(); + } + + @ParameterizedTest + @MethodSource + public void test_description(CommandOutputControlSpec spec) { + // This test is mostly for coverage. + var desc = spec.create().description(); + assertFalse(desc.isBlank()); + } + + @Test + public void test_copy() { + var orig = new CommandOutputControl(); + var copy = orig.copy(); + assertNotSame(orig, copy); + } + + @ParameterizedTest + @EnumSource(names = "SAVE_NOTHING", mode = Mode.EXCLUDE) + public void test_flag(OutputControl flag) { + var coc = new CommandOutputControl(); + assertFalse(flag.get(coc)); + flag.set(coc); + assertTrue(flag.get(coc)); + if (flag.canUnset()) { + flag.unset(coc); + assertFalse(flag.get(coc)); + } + } + + @ParameterizedTest + @MethodSource + public void test_mutual_exclusive_flags(List controls) { + if (controls.isEmpty()) { + throw new IllegalArgumentException(); + } + + var coc = new CommandOutputControl(); + for (var c : controls) { + c.set(coc); + } + + for (var c : controls.subList(0, controls.size() - 1)) { + assertFalse(c.get(coc)); + } + assertTrue(controls.getLast().get(coc)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_ExecutableAttributes(boolean toolProvider) { + var coc = new CommandOutputControl(); + CommandOutputControl.Executable exec; + if (toolProvider) { + exec = coc.createExecutable(new ToolProvider() { + + @Override + public String name() { + return "runme"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + fail("Should never be called"); + return 0; + } + + }, "--foo", "--baz=10"); + } else { + exec = coc.createExecutable(new ProcessBuilder("runme", "--foo", "--baz=10")); + } + + assertEquals("runme --foo --baz=10", exec.attributes().printableCommandLine()); + } + + @Test + public void test_Result_single_arg_ctor() { + var result = new CommandOutputControl.Result(7); + assertFalse(result.findContent().isPresent()); + assertFalse(result.findStdout().isPresent()); + assertFalse(result.findStderr().isPresent()); + assertEquals(7, result.getExitCode()); + assertSame(Objects.requireNonNull(CommandOutputControl.EMPTY_EXECUTABLE_ATTRIBUTES), result.execAttrs()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Result_unexpected(boolean customMessage) throws IOException { + var result = new CommandOutputControl.Result(7); + + if (customMessage) { + assertEquals("Kaput!", result.unexpected("Kaput!").getMessage()); + } else { + assertEquals("Unexpected result from executing the command ", result.unexpected().getMessage()); + } + } + + @Test + public void test_Result_expectExitCode() throws IOException { + var result = new CommandOutputControl.Result(7); + + assertSame(result, result.expectExitCode(7)); + assertSame(result, result.expectExitCode(7, 2)); + assertSame(result, result.expectExitCode(2, 7)); + + assertSame(result, result.expectExitCode(List.of(7))); + assertSame(result, result.expectExitCode(Set.of(7, 2))); + assertSame(result, result.expectExitCode(List.of(2, 7))); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Result_expectExitCode_negative(boolean collection) { + var result = new CommandOutputControl.Result(3); + + var ex = assertThrowsExactly(CommandOutputControl.UnexpectedExitCodeException.class, () -> { + if (collection) { + result.expectExitCode(List.of(17, 12)); + } else { + result.expectExitCode(17, 12); + } + }); + + assertNull(ex.getCause()); + assertSame(result, ex.getResult()); + assertEquals("Unexpected exit code 3 from executing the command ", ex.getMessage()); + } + + @ParameterizedTest + @MethodSource + public void test_Result_toCharacterResult(ToCharacterResultTestSpec spec) throws IOException, InterruptedException { + spec.test(); + } + + @Test + public void test_Result_toCharacterResult_nop() throws IOException, InterruptedException { + + var charset = StandardCharsets.UTF_8; + + var emptyResult = new CommandOutputControl.Result(7); + assertSame(emptyResult, emptyResult.toCharacterResult(charset, true)); + assertSame(emptyResult, emptyResult.toCharacterResult(charset, false)); + + var coc = new CommandOutputControl().saveOutput(true); + + var result = coc.createExecutable(new Command(List.of("foo"), List.of()).asToolProvider()).execute(); + + assertSame(result, result.toCharacterResult(charset, true)); + assertSame(result, result.toCharacterResult(charset, false)); + } + + @Test + public void test_Result_toCharacterResult_copyWithExecutableAttributes() { + + var empty = new CommandOutputControl.Result(0); + + var execAttrs = new CommandOutputControl.ExecutableAttributes() { + @Override + public List commandLine() { + return List.of("foo"); + } + }; + + var copy = empty.copyWithExecutableAttributes(execAttrs); + + assertSame(empty.exitCode(), copy.exitCode()); + assertSame(empty.output(), copy.output()); + assertSame(empty.byteOutput(), copy.byteOutput()); + assertSame(execAttrs, copy.execAttrs()); + } + + @Test + public void test_UnexpectedExitCodeException_no_exit_code() { + + var resultWithoutExitCode = CommandOutputControl.Result.build().create(); + + assertThrowsExactly(IllegalArgumentException.class, () -> { + new CommandOutputControl.UnexpectedExitCodeException(resultWithoutExitCode); + }); + + assertThrowsExactly(IllegalArgumentException.class, () -> { + new CommandOutputControl.UnexpectedExitCodeException(resultWithoutExitCode, "Kaput!"); + }); + } + + @ParameterizedTest + @EnumSource(ExecutableType.class) + public void test_timeout_expires(ExecutableType mode) throws InterruptedException, IOException { + + final var toolProvider = (mode == ExecutableType.TOOL_PROVIDER); + final var storeOutputInFiles = (mode == ExecutableType.PROCESS_BUILDER_WITH_STREAMS_IN_FILES); + + var actions = List.of( + CommandAction.echoStdout("The quick brown fox jumps"), + CommandAction.sleep(5), + CommandAction.echoStdout("over the lazy dog") + ); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true).storeOutputInFiles(storeOutputInFiles); + + CommandOutputControl.Executable exec; + + InterruptibleToolProvider tp; + + if (toolProvider) { + tp = new InterruptibleToolProvider(Command.createToolProvider(actions)); + exec = coc.createExecutable(tp); + } else { + var cmdline = Command.createShellCommandLine(actions); + tp = null; + exec = coc.createExecutable(new ProcessBuilder(cmdline)); + } + + var result = exec.execute(1, TimeUnit.SECONDS); + assertFalse(result.exitCode().isPresent()); + + var getExitCodeEx = assertThrowsExactly(IllegalStateException.class, result::getExitCode); + assertEquals(("Exit code is unavailable for timed-out command"), getExitCodeEx.getMessage()); + + // We want to check that the saved output contains only the text emitted before the "sleep" action. + // It works for a subprocess, but in the case of a ToolProvider, sometimes the timing is such + // that it gets interrupted before having written anything to the stdout, and the saved output is empty. + // This happens when the test case is executed together with other test cases + // and never when it is executed individually. + if (!toolProvider || !result.content().isEmpty()) { + assertEquals(List.of("The quick brown fox jumps"), result.content()); + } + + if (toolProvider) { + assertTrue(tp.interrupted()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_timeout(boolean toolProvider) throws InterruptedException, IOException { + + var actions = List.of( + CommandAction.echoStdout("Sphinx of black quartz,"), + CommandAction.echoStdout("judge my vow") + ); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true); + + CommandOutputControl.Executable exec; + + if (toolProvider) { + var tp = Command.createToolProvider(actions); + exec = coc.createExecutable(tp); + } else { + var cmdline = Command.createShellCommandLine(actions); + exec = coc.createExecutable(new ProcessBuilder(cmdline)); + } + + var result = exec.execute(10, TimeUnit.SECONDS); + assertTrue(result.exitCode().isPresent()); + assertEquals(List.of("Sphinx of black quartz,", "judge my vow"), result.content()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_passthrough_exceptions(boolean withTimeout) throws IOException { + + var expected = new RuntimeException("Kaput!"); + + var exec = new CommandOutputControl().createExecutable(new ToolProvider() { + + @Override + public String name() { + return "foo"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw expected; + } + }); + + var actual = assertThrowsExactly(expected.getClass(), () -> { + if (withTimeout) { + exec.execute(10, TimeUnit.SECONDS); + } else { + exec.execute(); + } + }); + + assertSame(expected, actual); + } + + @Test + public void test_externally_terminated() throws InterruptedException, IOException { + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.echoStderr("The five boxing wizards"), + CommandAction.sleep(10), + CommandAction.echoStderr("jump quickly") + )); + + var processDestroyer = Slot.>createEmpty(); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true).processListener(process -> { + // Once we are notified the process has been started, schedule its destruction. + // Give it a second to warm up and print some output and then destroy it. + processDestroyer.set(CompletableFuture.runAsync(toRunnable(() -> { + Thread.sleep(Duration.ofSeconds(1)); + // On Windows, CommandAction#sleep is implemented with the "ping" command. + // By some reason, when the parent "cmd" process is destroyed, + // the child "ping" command stays alive, and the test waits when it completes, + // making it last for at least 10 seconds. + // To optimize the test work time, destroy the entire subprocess tree. + // Even though this is essential on Windows keep this logic on all platforms for simplicity. + var descendants = List.of(); + try (var descendantsStream = process.descendants()) { + descendants = descendantsStream.toList(); + } finally { + process.destroyForcibly(); + } + descendants.forEach(ProcessHandle::destroyForcibly); + }))); + }); + var exec = coc.createExecutable(new ProcessBuilder(cmdline)); + + var result = exec.execute(); + assertNotEquals(0, result.getExitCode()); + assertEquals(List.of("The five boxing wizards"), result.content()); + processDestroyer.get().join(); + } + + @DisabledOnOs(value = OS.MAC, disabledReason = "Closing a stream doesn't consistently cause a trouble as it should") + @ParameterizedTest + @EnumSource(OutputStreams.class) + public void test_close_streams(OutputStreams action) throws InterruptedException, IOException { + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.echoStdout("Hello stdout"), + CommandAction.echoStderr("Bye stderr") + )); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true).processListener(toConsumer(process -> { + // Close process output stream(s). This should make corresponding stream gobbler(s) throw IOException. + switch (action) { + case STDOUT -> { + process.getInputStream().close(); + } + case STDERR -> { + process.getErrorStream().close(); + } + case STDOUT_AND_STDERR -> { + process.getInputStream().close(); + process.getErrorStream().close(); + } + } + })); + var exec = coc.createExecutable(new ProcessBuilder(cmdline)); + + var ex = assertThrows(IOException.class, exec::execute); + System.out.println("test_close_streams: " + action); + ex.printStackTrace(System.out); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_interleaved(boolean customDumpStreams) throws IOException, InterruptedException { + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.echoStdout("Eat some more"), + CommandAction.echoStderr("of these"), + CommandAction.echoStdout("soft French pastries"), + CommandAction.echoStderr("and drink some tea") + )); + + var coc = new CommandOutputControl(); + var exec = coc.createExecutable(new ProcessBuilder(cmdline)); + + coc.saveOutput(true).dumpOutput(true); + + CommandOutputControl.Result result; + + if (customDumpStreams) { + // Execute the command so that its stdout and stderr are dumped to the same sink. + var sink = new ByteArrayOutputStream(); + var ps = new PrintStream(sink); + + coc.dumpStdout(ps).dumpStderr(ps); + + result = exec.execute(); + + var commandStdout = List.of("Eat some more", "soft French pastries"); + var commandStderr = List.of("of these", "and drink some tea"); + + var sinkContent = toStringList(sink.toByteArray(), StandardCharsets.US_ASCII); + + if (!isInterleave(sinkContent, commandStdout, commandStderr)) { + fail(String.format("Unexpected combined output=%s; stdout=%s; stderr=%s", + sinkContent, commandStdout, commandStderr)); + } + + // CommandOutputControl was not configured to redirect stderr in stdout, + // hence the output is ordered: stdout goes first, stderr follows. + assertEquals(Stream.of(commandStdout, commandStderr).flatMap(List::stream).toList(), result.content()); + + // Saved stdout an stderr can be accessed individually. + assertEquals(commandStdout, result.stdout()); + assertEquals(commandStderr, result.stderr()); + } else { + // Execute the command so that its stdout and stderr are dumped into System.out. + coc.redirectStderr(true); + result = exec.execute(); + + // CommandOutputControl was configured to redirect stderr in stdout, + // hence the output is interleaved. + assertEquals(List.of("Eat some more", "of these", "soft French pastries", "and drink some tea"), result.content()); + + // Saved stdout an stderr can NOT be accessed individually because they are interleaved. + assertTrue(result.findStdout().isEmpty()); + assertTrue(result.findStderr().isEmpty()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true}) + public void stressTest(boolean binaryOutput, @TempDir Path workDir) throws Exception { + + // Execute multiple subprocesses asynchronously. + // Each subprocess writes a few chunks of data each larger than the default buffer size (8192 bytes) + + final var chunkCount = 5; + final var subprocessCount = 100; + final var subprocessExecutor = Executors.newVirtualThreadPerTaskExecutor(); + + final var md = MessageDigest.getInstance("MD5"); + + var cmdline = Command.createShellCommandLine(IntStream.range(0, chunkCount).mapToObj(chunk -> { + byte[] bytes = new byte[10 * 1024]; // 10K to exceed the default BufferedOutputStream's buffer size of 8192. + new Random().nextBytes(bytes); + md.update(bytes); + var path = workDir.resolve(Integer.toString(chunk)); + try { + Files.write(path, bytes); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return path; + }).map(CommandAction::cat).toList()); + + final var digest = HexFormat.of().formatHex(md.digest()); + + // Schedule to start every subprocess in a separate virtual thread. + // Start and suspend threads, waiting until all scheduled threads have started. + // After all scheduled threads start, resume them. + // This should result in starting all scheduled subprocesses simultaneously. + + var readyLatch = new CountDownLatch(subprocessCount); + var startLatch = new CountDownLatch(1); + + var futures = IntStream.range(0, subprocessCount).mapToObj(_ -> { + return CompletableFuture.supplyAsync(toSupplier(() -> { + + var exec = new CommandOutputControl() + .saveOutput(true) + .binaryOutput(binaryOutput) + .createExecutable(new ProcessBuilder(cmdline)); + + readyLatch.countDown(); + startLatch.await(); + + var result = exec.execute(); + + var localMd = MessageDigest.getInstance("MD5"); + localMd.update(result.byteContent()); + + return HexFormat.of().formatHex(localMd.digest()); + + }), subprocessExecutor); + }).toList(); + + readyLatch.await(); + startLatch.countDown(); + + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); + + futures.forEach(future -> { + var actualDigest = future.join(); + assertEquals(digest, actualDigest); + }); + } + + public enum OutputStreams { + STDOUT, + STDERR, + STDOUT_AND_STDERR + } + + private static List test_description() { + List testCases = new ArrayList<>(); + testCases.add(new CommandOutputControlSpec(Set.of())); + for (var outputControl : OutputControl.variants()) { + testCases.add(new CommandOutputControlSpec(outputControl)); + } + return testCases; + } + + private static List> test_mutual_exclusive_flags() { + List> data = new ArrayList<>(); + + var flags = List.of(OutputControl.SAVE_ALL, OutputControl.SAVE_FIRST_LINE, OutputControl.SAVE_NOTHING); + + List seq = new ArrayList<>(); + for (var _1 : flags) { + seq.add(_1); + var flags2 = flags.stream().filter(Predicate.isEqual(_1).negate()).toList(); + for (var _2 : flags2) { + seq.add(_2); + var flags3 = flags2.stream().filter(Predicate.isEqual(_2).negate()).toList(); + for (var _3 : flags3) { + seq.add(_3); + data.add(List.copyOf(seq)); + seq.removeLast(); + } + seq.removeLast(); + } + seq.removeLast(); + } + + return data; + } + + public record ToCharacterResultTestSpec(OutputTestSpec execSpec, boolean keepByteContent) { + + public ToCharacterResultTestSpec { + Objects.requireNonNull(execSpec); + } + + @Override + public String toString() { + final List tokens = new ArrayList<>(); + + tokens.add(execSpec.toString()); + if (keepByteContent) { + tokens.add("keepByteContent"); + } + + return String.join(", ", tokens.toArray(String[]::new)); + } + + void test() throws IOException, InterruptedException { + var coc = execSpec.cocSpec().create(); + + var command = execSpec.commandSpec().command().asToolProvider(); + + var expected = coc.binaryOutput(false).createExecutable(command).execute(); + + var byteResult = coc.binaryOutput(true).createExecutable(command).execute(); + + var actual = byteResult.toCharacterResult(coc.charset(), keepByteContent); + + CommandOutputControl.Result expectedByteContent; + if (keepByteContent) { + expectedByteContent = byteResult; + } else { + expectedByteContent = expected; + } + + assertArrayEquals(expectedByteContent.findByteContent().orElse(null), actual.findByteContent().orElse(null)); + assertArrayEquals(expectedByteContent.findByteStdout().orElse(null), actual.findByteStdout().orElse(null)); + assertArrayEquals(expectedByteContent.findByteStderr().orElse(null), actual.findByteStderr().orElse(null)); + + assertEquals(expected.findContent(), actual.findContent()); + assertEquals(expected.findStdout(), actual.findStdout()); + assertEquals(expected.findStderr(), actual.findStderr()); + + assertSame(byteResult.execAttrs(), actual.execAttrs()); + assertEquals(expected.exitCode(), actual.exitCode()); + } + } + + private static Stream test_Result_toCharacterResult() { + List testCases = new ArrayList<>(); + + var skip = Set.of(OutputControl.BINARY_OUTPUT, OutputControl.DUMP, OutputControl.SAVE_FIRST_LINE); + + for (var outputControl : OutputControl.variants().stream().filter(spec -> { + return !skip.stream().anyMatch(spec::contains); + }).toList()) { + for (var stdoutContent : List.of(OutputData.EMPTY, OutputData.MANY)) { + for (var stderrContent : List.of(OutputData.EMPTY, OutputData.MANY)) { + var commandSpec = new CommandSpec(stdoutContent, stderrContent); + testCases.add(new OutputTestSpec(false, new CommandOutputControlSpec(outputControl), commandSpec)); + } + } + } + + return testCases.stream().flatMap(execSpec -> { + return Stream.of(true, false).map(keepByteContent -> { + return new ToCharacterResultTestSpec(execSpec, keepByteContent); + }); + }); + } + + private static boolean cherryPickSavedOutputTestCases() { + return !testSomeSavedOutput().isEmpty(); + } + + /** + * Returns test cases for {@link #testSomeSavedOutput(OutputTestSpec)}. + *

    + * Aimed to simplify debugging of {@link #OutputTestSpec} test cases. + *

    + * The total number of {@code #OutputTestSpec} test cases is ~1500. When some + * fail and need debugging, it is a waste of time to run them all. This method + * allows running only selected test cases. It works this way: + *

      + *
    • Run CommandOutputControlTest test. + *
    • If some {@linke #testSavedOutput(OutputTestSpec)} invocations fail, + * capture their IDs (test case ID is an index starting from 1). + *
    • Replace "/* 10, 67, 456 */" comment in the body of this method with + * the captured test case IDs. + *
    • Rerun CommandOutputControlTest test. This time, it will run + * {@link #testSomeSavedOutput(OutputTestSpec)} method instead of + * {@link #testSavedOutput(OutputTestSpec)} with the list of the captured test + * case IDs. + *
    + */ + private static List testSomeSavedOutput() { + var testIds = List.of(/* 10, 67, 456 */); + if (testIds.isEmpty()) { + return List.of(); + } else { + var allTestCases = testSavedOutput(); + return testIds.stream().map(testId -> { + return allTestCases.get(testId - 1); + }).toList(); + } + } + + private static List testSavedOutput() { + List testCases = new ArrayList<>(); + for (final var executableType : List.of(ExecutableType.values())) { + for (var outputControl : OutputControl.variants()) { + for (final var stdoutContent : List.of(OutputData.values())) { + for (final var stderrContent : List.of(OutputData.values())) { + + if (outputControl.contains(OutputControl.BINARY_OUTPUT) + && (stdoutContent == OutputData.ONE_LINE || stderrContent == OutputData.ONE_LINE)) { + // Skip a test case if it runs a command writing + // a single line in stdout or stderr, and handles command output as a byte stream. + // It duplicates test cases that write multiple lines in stdout or stderr. + continue; + } + + final var commandSpec = new CommandSpec(stdoutContent, stderrContent); + boolean toolProvider; + switch (executableType) { + case PROCESS_BUILDER -> { + toolProvider = false; + } + case PROCESS_BUILDER_WITH_STREAMS_IN_FILES -> { + outputControl = new SetBuilder() + .add(outputControl) + .add(OutputControl.STORE_STREAMS_IN_FILES) + .create(); + toolProvider = false; + } + case TOOL_PROVIDER -> { + toolProvider = true; + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + testCases.add(new OutputTestSpec( + toolProvider, + new CommandOutputControlSpec(outputControl), + commandSpec)); + } + } + } + } + return testCases; + } + + private static List testDumpStreams() { + List testCases = new ArrayList<>(); + final var commandSpec = new CommandSpec(OutputData.MANY, OutputData.MANY); + for (var discardStdout : withAndWithout(OutputControl.DISCARD_STDOUT)) { + for (var discardStderr : withAndWithout(OutputControl.DISCARD_STDERR)) { + for (var redirectStderr : withAndWithout(OutputControl.REDIRECT_STDERR)) { + for (var binaryOutput : withAndWithout(OutputControl.BINARY_OUTPUT)) { + for (var dumpStdout : withAndWithout(OutputControl.DUMP_STDOUT_IN_SYSTEM_OUT)) { + for (var dumpStderr : withAndWithout(OutputControl.DUMP_STDERR_IN_SYSTEM_ERR)) { + + if (dumpStderr.isEmpty() && dumpStdout.isEmpty()) { + // Output dumping disabled + continue; + } + + if (discardStderr.isPresent() && discardStdout.isPresent()) { + // Output dumping enabled, but all stream discarded + continue; + } + + if (dumpStderr.isPresent() == discardStderr.isPresent() && dumpStdout.isEmpty()) { + // Stderr dumping enabled but discarded, stdout dumping disabled + continue; + } + + if (dumpStdout.isPresent() == discardStdout.isPresent() && dumpStderr.isEmpty()) { + // Stdout dumping enabled but discarded, stderr dumping disabled + continue; + } + + final var outputControl = new HashSet(); + outputControl.add(OutputControl.DUMP); + discardStdout.ifPresent(outputControl::add); + discardStderr.ifPresent(outputControl::add); + redirectStderr.ifPresent(outputControl::add); + binaryOutput.ifPresent(outputControl::add); + dumpStdout.ifPresent(outputControl::add); + dumpStderr.ifPresent(outputControl::add); + + testCases.add(new OutputTestSpec( + false, + new CommandOutputControlSpec(outputControl), + commandSpec)); + } + } + } + } + } + } + return testCases; + } + + private static List testCharset() { + List testCases = new ArrayList<>(); + + 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))); + } + } + } + + return testCases; + } + + private enum ExecutableType { + TOOL_PROVIDER, + PROCESS_BUILDER, + PROCESS_BUILDER_WITH_STREAMS_IN_FILES, + ; + } + + private sealed interface CommandAction { + static SleepCommandAction sleep(int seconds) { + return new SleepCommandAction(seconds); + } + + static EchoCommandAction echoStdout(String str) { + return new EchoCommandAction(str, false); + } + + static EchoCommandAction echoStderr(String str) { + return new EchoCommandAction(str, true); + } + + static WriteCommandAction writeStdout(byte[] binary) { + return new WriteCommandAction(binary, false); + } + + static WriteCommandAction writeStderr(byte[] binary) { + return new WriteCommandAction(binary, true); + } + + static CatCommandAction cat(Path file) { + return new CatCommandAction(file); + } + } + + private record EchoCommandAction(String value, boolean stderr) implements CommandAction { + EchoCommandAction { + Objects.requireNonNull(value); + } + } + + private record WriteCommandAction(byte[] value, boolean stderr) implements CommandAction { + WriteCommandAction { + Objects.requireNonNull(value); + } + } + + private record CatCommandAction(Path file) implements CommandAction { + CatCommandAction { + Objects.requireNonNull(file); + } + } + + private record SleepCommandAction(int seconds) implements CommandAction { + SleepCommandAction { + if (seconds < 0) { + throw new IllegalArgumentException(); + } + } + } + + private record Command(List stdout, List stderr) { + Command { + stdout.forEach(Objects::requireNonNull); + stderr.forEach(Objects::requireNonNull); + } + + List asExecutable() { + return createShellCommandLine(actions()); + } + + ToolProvider asToolProvider() { + return createToolProvider(actions()); + } + + // + // Type of shell for which to create a command line. + // On Unix it is always the "sh". + // On Windows, it is "cmd" by default and "powershell" when a command needs to write binary data to output stream(s). + // Extra complexity on Windows is because "powershell" is times slower than "cmd", + // and the latter doesn't support binary output. + // + private enum ShellType { + SH(OperatingSystem.LINUX, OperatingSystem.MACOS), + CMD(OperatingSystem.WINDOWS), + POWERSHELL(OperatingSystem.WINDOWS), + ; + + ShellType(OperatingSystem... os) { + if (os.length == 0) { + throw new IllegalArgumentException(); + } + this.os = Set.of(os); + } + + boolean isSupportedOnCurrentOS() { + return os.contains(OperatingSystem.current()); + } + + private final Set os; + } + + private List actions() { + return Stream.concat( + stdout.stream().map(CommandAction::echoStdout), + stderr.stream().map(CommandAction::echoStderr) + ).toList(); + } + + static List createShellCommandLine(List actions) { + final var shellType = detectShellType(actions); + final List commandline = new ArrayList<>(); + final String commandSeparator; + switch (shellType) { + case SH -> { + commandline.addAll(List.of("sh", "-c")); + commandSeparator = " && "; + } + case CMD -> { + commandline.addAll(List.of("cmd", "/C")); + commandSeparator = " && "; + } + case POWERSHELL -> { + commandline.addAll(List.of("powershell", "-NoProfile", "-Command")); + commandSeparator = "; "; + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + commandline.add(actions.stream().map(action -> { + return Command.toString(action, shellType); + }).collect(joining(commandSeparator))); + return commandline; + } + + static ToolProvider createToolProvider(List actions) { + var copiedActions = List.copyOf(actions); + return new ToolProvider() { + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public int run(PrintStream out, PrintStream err, String... args) { + for (var action : copiedActions) { + switch (action) { + case EchoCommandAction echo -> { + if (echo.stderr()) { + err.println(echo.value()); + } else { + out.println(echo.value()); + } + } + case WriteCommandAction write -> { + try { + if (write.stderr()) { + err.write(write.value()); + } else { + out.write(write.value()); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + case SleepCommandAction sleep -> { + toRunnable(() -> { + synchronized (this) { + var millis = Duration.ofSeconds(sleep.seconds()).toMillis(); + this.wait(millis); + } + }).run(); + } + case CatCommandAction _ -> { + // Not used, no point to implement. + throw new UnsupportedOperationException(); + } + } + } + return 0; + } + + @Override + public String name() { + return "test"; + } + }; + } + + private static ShellType detectShellType(List actions) { + var supportedShellTypes = Stream.of(ShellType.values()) + .filter(ShellType::isSupportedOnCurrentOS) + .collect(Collectors.toCollection(HashSet::new)); + for (var action : actions) { + if (action instanceof WriteCommandAction) { + supportedShellTypes.remove(ShellType.CMD); + } + } + return supportedShellTypes.stream() + .sorted(Comparator.comparingInt(Enum::ordinal)) + .findFirst().orElseThrow(); + } + + private static String toString(CommandAction action, ShellType shellType) { + switch (action) { + case EchoCommandAction a -> { + return toString(a, shellType); + } + case WriteCommandAction a -> { + return toString(a, shellType); + } + case SleepCommandAction a -> { + return toString(a, shellType); + } + case CatCommandAction a -> { + return toString(a, shellType); + } + } + } + + private static String toString(EchoCommandAction echo, ShellType shellType) { + String str; + switch (shellType) { + case SH -> { + str = "echo " + echo.value(); + if (echo.stderr()) { + str += ">&2"; + } + } + case CMD -> { + str = "(echo " + echo.value() + ")"; + if (echo.stderr()) { + str += ">&2"; + } + } + case POWERSHELL -> { + str = String.format("[Console]::%s.WriteLine(\\\"%s\\\")", + echo.stderr() ? "Error" : "Out", echo.value()); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + return str; + } + + private static String toString(WriteCommandAction write, ShellType shellType) { + String str; + switch (shellType) { + case SH -> { + // Convert byte[] to octal string to make it work with POSIX printf. + // POSIX printf doesn't recognize hex strings, so can't use handy HexFormat. + var sb = new StringBuilder(); + sb.append("printf "); + for (var b : write.value()) { + sb.append("\\\\").append(Integer.toOctalString(b & 0xFF)); + } + if (write.stderr()) { + sb.append(">&2"); + } + str = sb.toString(); + } + case CMD -> { + throw new UnsupportedOperationException("Can't output binary data with 'cmd'"); + } + case POWERSHELL -> { + var base64 = Base64.getEncoder().encodeToString(write.value()); + str = String.format( + "$base64 = '%s'; " + + "$bytes = [Convert]::FromBase64String($base64); " + + "[Console]::%s().Write($bytes, 0, $bytes.Length)", + base64, write.stderr() ? "OpenStandardError" : "OpenStandardOutput"); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + return str; + } + + private static String toString(SleepCommandAction sleep, ShellType shellType) { + switch (shellType) { + case SH -> { + return "sleep " + sleep.seconds(); + } + case CMD -> { + // The standard way to sleep in "cmd" is to use the "ping" command. + // It sends packets every second. + // To wait N seconds, it should send N+1 packets. + // The "timeout" command works only in a console. + return String.format("(ping -n %d localhost > nul)", sleep.seconds() + 1); + } + case POWERSHELL -> { + return "Start-Sleep -Seconds " + sleep.seconds(); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + + private static String toString(CatCommandAction cat, ShellType shellType) { + switch (shellType) { + case SH -> { + return "cat " + cat.file(); + } + case CMD -> { + return "type " + cat.file(); + } + case POWERSHELL -> { + // Not used, no point to implement. + throw new UnsupportedOperationException(); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + + } + + private enum OutputData { + EMPTY(List.of()), + ONE_LINE(List.of("Jupiter")), + MANY(List.of("Uranus", "Saturn", "Earth")); + + OutputData(List data) { + data.forEach(Objects::requireNonNull); + this.data = data; + } + + final List data; + } + + private record CommandSpec(OutputData stdout, OutputData stderr) { + CommandSpec { + Objects.requireNonNull(stdout); + Objects.requireNonNull(stderr); + } + + @Override + public String toString() { + return String.format("[stdout=%s, stderr=%s]", stdout, stderr); + } + + Command command() { + return new Command(stdout.data.stream().map(line -> { + return "stdout." + line; + }).toList(), stderr.data.stream().map(line -> { + return "stderr." + line; + }).toList()); + } + } + + public interface CommandOutputControlMutator { + String name(); + void mutate(CommandOutputControl coc); + + static Function> addToSet(Set set) { + return m -> { + return new SetBuilder().add(set).add(m).create(); + }; + } + } + + public enum OutputControl implements CommandOutputControlMutator { + DUMP(CommandOutputControl::dumpOutput, CommandOutputControl::isDumpOutput), + SAVE_ALL(CommandOutputControl::saveOutput, CommandOutputControl::isSaveOutput), + SAVE_FIRST_LINE(CommandOutputControl::saveFirstLineOfOutput, CommandOutputControl::isSaveFirstLineOfOutput), + SAVE_NOTHING(coc -> { + coc.saveOutput(false); + }, coc -> { + return !coc.isSaveOutput() && !coc.isSaveFirstLineOfOutput(); + }), + DISCARD_STDOUT(CommandOutputControl::discardStdout, CommandOutputControl::isDiscardStdout), + DISCARD_STDERR(CommandOutputControl::discardStderr, CommandOutputControl::isDiscardStderr), + REDIRECT_STDERR(CommandOutputControl::redirectStderr, CommandOutputControl::isRedirectStderr), + STORE_STREAMS_IN_FILES(CommandOutputControl::storeOutputInFiles, CommandOutputControl::isStoreOutputInFiles), + BINARY_OUTPUT(CommandOutputControl::binaryOutput, CommandOutputControl::isBinaryOutput), + DUMP_STDOUT_IN_SYSTEM_OUT(coc -> { + coc.dumpStdout(new PrintStreamWrapper(System.out)); + }, coc -> { + return coc.dumpStdout() instanceof PrintStreamWrapper; + }), + DUMP_STDERR_IN_SYSTEM_ERR(coc -> { + coc.dumpStderr(new PrintStreamWrapper(System.err)); + }, coc -> { + return coc.dumpStderr() instanceof PrintStreamWrapper; + }), + CHARSET_UTF16LE(coc -> { + coc.charset(StandardCharsets.UTF_16LE); + }, coc -> { + return coc.charset() == StandardCharsets.UTF_16LE; + }), + ; + + OutputControl(Consumer setter, Function getter) { + this.setter = Objects.requireNonNull(setter); + this.unsetter = null; + this.getter = Objects.requireNonNull(getter); + } + + OutputControl(BiConsumer setter, Function getter) { + Objects.requireNonNull(setter); + this.setter = coc -> { + setter.accept(coc, true); + }; + this.unsetter = coc -> { + setter.accept(coc, false); + }; + this.getter = Objects.requireNonNull(getter); + } + + @Override + public void mutate(CommandOutputControl coc) { + set(coc); + } + + CommandOutputControl set(CommandOutputControl coc) { + setter.accept(coc); + return coc; + } + + CommandOutputControl unset(CommandOutputControl coc) { + Objects.requireNonNull(unsetter).accept(coc); + return coc; + } + + boolean canUnset() { + return unsetter != null; + } + + boolean get(CommandOutputControl coc) { + return getter.apply(coc); + } + + static List> variants() { + final List> variants = new ArrayList<>(); + for (final var binaryOutput : withAndWithout(BINARY_OUTPUT)) { + for (final var redirectStderr : withAndWithout(REDIRECT_STDERR)) { + for (final var withDump : withAndWithout(DUMP)) { + variants.addAll(Stream.of( + Set.of(), + Set.of(SAVE_ALL), + Set.of(SAVE_FIRST_LINE), + Set.of(DISCARD_STDOUT), + Set.of(DISCARD_STDERR), + Set.of(SAVE_ALL, DISCARD_STDOUT), + Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT), + Set.of(SAVE_ALL, DISCARD_STDERR), + Set.of(SAVE_FIRST_LINE, DISCARD_STDERR), + Set.of(SAVE_ALL, DISCARD_STDOUT, DISCARD_STDERR), + Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT, DISCARD_STDERR) + ).map(v -> { + return withDump.map(CommandOutputControlMutator.addToSet(v)).orElse(v); + }).map(v -> { + return redirectStderr.filter(_ -> { + return !v.containsAll(List.of(DISCARD_STDOUT, DISCARD_STDERR)); + }).map(CommandOutputControlMutator.addToSet(v)).orElse(v); + }).map(v -> { + return binaryOutput.map(CommandOutputControlMutator.addToSet(v)).orElse(v); + }).toList()); + } + } + } + return variants.stream().distinct().toList(); + } + + private static final class PrintStreamWrapper extends PrintStream { + PrintStreamWrapper(PrintStream out) { + super(out, true); + } + } + + private final Consumer setter; + private final Consumer unsetter; + private final Function getter; + + static final Set SAVE = Set.of(SAVE_ALL, SAVE_FIRST_LINE); + } + + public record CommandOutputControlSpec(Set outputControl) { + public CommandOutputControlSpec { + outputControl.forEach(Objects::requireNonNull); + if (outputControl.containsAll(OutputControl.SAVE)) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + return outputControl.stream().map(CommandOutputControlMutator::name).sorted().collect(joining("+")); + } + + boolean contains(OutputControl v) { + return outputControl.contains(Objects.requireNonNull(v)); + } + + boolean dumpOutput() { + return contains(OutputControl.DUMP); + } + + boolean saveOutput() { + return !Collections.disjoint(outputControl, OutputControl.SAVE); + } + + boolean discardStdout() { + return contains(OutputControl.DISCARD_STDOUT); + } + + boolean discardStderr() { + return contains(OutputControl.DISCARD_STDERR); + } + + boolean redirectStderr() { + return contains(OutputControl.REDIRECT_STDERR); + } + + CommandOutputControl create() { + final CommandOutputControl coc = new CommandOutputControl(); + outputControl.forEach(control -> control.mutate(coc)); + return coc; + } + } + + public record OutputTestSpec(boolean toolProvider, CommandOutputControlSpec cocSpec, CommandSpec commandSpec) { + public OutputTestSpec { + Objects.requireNonNull(cocSpec); + Objects.requireNonNull(commandSpec); + } + + @Override + public String toString() { + final List tokens = new ArrayList<>(); + + if (toolProvider) { + tokens.add("tool-provider"); + } + + tokens.add("output=" + cocSpec.toString()); + tokens.add("command=" + commandSpec); + + return String.join(", ", tokens.toArray(String[]::new)); + } + + void test() { + final var command = commandSpec.command(); + + final Slot result = Slot.createEmpty(); + final var dumpCapture = DumpCapture.captureDump(toRunnable(() -> { + result.set(createExecutable(command).execute()); + })); + + assertEquals(0, result.get().getExitCode()); + + verifyDump(dumpCapture, command); + if (contains(OutputControl.BINARY_OUTPUT)) { + verifyByteResultContent(result.get(), command, StandardCharsets.UTF_8); + } else { + verifyResultContent(result.get(), command); + } + } + + private boolean contains(OutputControl v) { + return cocSpec.contains(v); + } + + private boolean dumpOutput() { + return cocSpec.dumpOutput(); + } + + private boolean saveOutput() { + return cocSpec.saveOutput(); + } + + private boolean discardStdout() { + return cocSpec.discardStdout(); + } + + private boolean discardStderr() { + return cocSpec.discardStderr(); + } + + private boolean redirectStderr() { + return cocSpec.redirectStderr(); + } + + private boolean replaceStdoutWithStderr() { + return redirectStderr() && discardStdout() && !discardStderr(); + } + + private boolean stdoutInherited() { + if (toolProvider || saveOutput() || replaceStdoutWithStderr()) { + return false; + } + return dumpOutput() && !discardStdout() && !contains(OutputControl.DUMP_STDOUT_IN_SYSTEM_OUT); + } + + private boolean stderrInherited() { + if (toolProvider || saveOutput() || redirectStderr()) { + return false; + } + return dumpOutput() && !discardStderr() && !contains(OutputControl.DUMP_STDERR_IN_SYSTEM_ERR); + } + + private void verifyDump(DumpCapture dumpCapture, Command command) { + if (!dumpOutput()) { + assertEquals(List.of(), dumpCapture.outLines()); + assertEquals(List.of(), dumpCapture.errLines()); + return; + } + + if (replaceStdoutWithStderr()) { + // STDERR replaces STDOUT + assertEquals(command.stderr(), dumpCapture.outLines()); + assertEquals(List.of(), dumpCapture.errLines()); + return; + } + + verifyDumpedStdout(dumpCapture, command); + verifyDumpedStderr(dumpCapture, command); + } + + private void verifyDumpedStdout(DumpCapture dumpCapture, Command command) { + if (stdoutInherited()) { + // A subprocess wrote its STDOUT into a file descriptor associated + // with the Java process's STDOUT, not into System.out. Can't capture it. + assertEquals(List.of(), dumpCapture.outLines()); + return; + } + + if (redirectStderr() && !discardStderr()) { + // Interleaved STDOUT and STDERR + if (!isInterleave(dumpCapture.outLines(), command.stdout(), command.stderr())) { + fail(String.format("Unexpected combined output=%s; stdout=%s; stderr=%s", + dumpCapture.outLines(), command.stdout(), command.stderr())); + } + } else if (discardStdout()) { + assertEquals(List.of(), dumpCapture.outLines()); + } else { + assertEquals(command.stdout(), dumpCapture.outLines()); + } + } + + private void verifyDumpedStderr(DumpCapture dumpCapture, Command command) { + if (stderrInherited()) { + // A subprocess wrote its STDERR into a file descriptor associated + // with the Java process's STDERR, not into System.err. Can't capture it. + assertEquals(List.of(), dumpCapture.errLines()); + return; + } + + if (redirectStderr() || discardStderr()) { + assertEquals(List.of(), dumpCapture.errLines()); + } else { + assertEquals(command.stderr(), dumpCapture.errLines()); + } + } + + private void verifyResultContent(CommandOutputControl.Result result, Command command) { + Objects.requireNonNull(result); + Objects.requireNonNull(command); + + assertTrue(result.findByteContent().isEmpty()); + assertTrue(result.findByteStdout().isEmpty()); + assertTrue(result.findByteStderr().isEmpty()); + + if (!saveOutput()) { + assertTrue(result.findContent().isEmpty()); + assertTrue(result.findStdout().isEmpty()); + assertTrue(result.findStderr().isEmpty()); + return; + } + + assertTrue(result.findContent().isPresent()); + + command = filterSavedStreams(command); + + var content = result.content(); + + if (contains(OutputControl.SAVE_FIRST_LINE)) { + assertTrue(content.size() <= 2, String.format("The number of saved lines must be less than or equal to two. Actual: %d", result.content().size())); + } + + if (!redirectStderr()) { + var stdout = result.stdout(); + var stderr = result.stderr(); + + assertEquals(command.stdout(), stdout); + assertEquals(command.stderr(), stderr); + assertEquals(Stream.of( + stdout, + stderr + ).flatMap(List::stream).toList(), content); + } else { + assertEquals(discardStderr(), result.findStdout().isPresent()); + assertTrue(result.findStderr().isEmpty()); + if (contains(OutputControl.SAVE_FIRST_LINE)) { + assertTrue(List.of(command.stdout(), command.stderr()).contains(result.content()), + String.format("Saved content %s is either %s or %s", + content, command.stdout(), command.stderr())); + } else if (contains(OutputControl.SAVE_ALL)) { + if (!isInterleave(content, command.stdout(), command.stderr())) { + fail(String.format("Unexpected combined saved content=%s; stdout=%s; stderr=%s", + content, command.stdout(), command.stderr())); + } + } else { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + + private void verifyByteResultContent(CommandOutputControl.Result result, Command command, Charset charset) { + Objects.requireNonNull(result); + Objects.requireNonNull(command); + Objects.requireNonNull(charset); + + assertTrue(result.findContent().isEmpty()); + assertTrue(result.findStdout().isEmpty()); + assertTrue(result.findStderr().isEmpty()); + + if (!saveOutput()) { + assertTrue(result.findByteContent().isEmpty()); + assertTrue(result.findByteStdout().isEmpty()); + assertTrue(result.findByteStderr().isEmpty()); + return; + } + + assertTrue(result.findByteContent().isPresent()); + + command = filterSavedStreams(command); + + if (!redirectStderr()) { + assertEquals(command.stdout(), toStringList(result.byteStdout(), charset)); + assertEquals(command.stderr(), toStringList(result.byteStderr(), charset)); + assertEquals(Stream.of( + command.stdout(), + command.stderr() + ).flatMap(List::stream).toList(), toStringList(result.byteContent(), charset)); + } else { + assertEquals(discardStderr(), result.findByteStdout().isPresent()); + assertTrue(result.findByteStderr().isEmpty()); + + var combined = toStringList(result.byteContent(), charset); + if (!isInterleave(combined, command.stdout(), command.stderr())) { + fail(String.format("Unexpected combined saved content=%s; stdout=%s; stderr=%s", + combined, command.stdout(), command.stderr())); + } + } + } + + private List expectedSavedStream(List commandOutput) { + Objects.requireNonNull(commandOutput); + if (contains(OutputControl.SAVE_ALL) || (contains(OutputControl.SAVE_FIRST_LINE) && contains(OutputControl.BINARY_OUTPUT))) { + return commandOutput; + } else if (contains(OutputControl.SAVE_FIRST_LINE)) { + return commandOutput.stream().findFirst().map(List::of).orElseGet(List::of); + } else { + throw new IllegalStateException(); + } + } + + private Command filterSavedStreams(Command command) { + return new Command( + (discardStdout() ? List.of() : expectedSavedStream(command.stdout())), + (discardStderr() ? List.of() : expectedSavedStream(command.stderr()))); + } + + private record DumpCapture(byte[] out, byte[] err, Charset outCharset, Charset errCharset) { + DumpCapture { + Objects.requireNonNull(out); + Objects.requireNonNull(err); + Objects.requireNonNull(outCharset); + Objects.requireNonNull(errCharset); + } + + List outLines() { + return toStringList(out, outCharset); + } + + List errLines() { + return toStringList(err, errCharset); + } + + static DumpCapture captureDump(Runnable runnable) { + final var captureOut = new ByteArrayOutputStream(); + final var captureErr = new ByteArrayOutputStream(); + + final var out = System.out; + final var err = System.err; + try { + final var outCharset = System.out.charset(); + final var errCharset = System.err.charset(); + System.setOut(new PrintStream(captureOut, true, outCharset)); + System.setErr(new PrintStream(captureErr, true, errCharset)); + runnable.run(); + return new DumpCapture(captureOut.toByteArray(), captureErr.toByteArray(), outCharset, errCharset); + } finally { + try { + System.setOut(out); + } finally { + System.setErr(err); + } + } + } + } + + private CommandOutputControl.Executable createExecutable(Command command) { + final var coc = cocSpec.create(); + if (toolProvider) { + return coc.createExecutable(command.asToolProvider()); + } else { + return coc.createExecutable(new ProcessBuilder(command.asExecutable())); + } + } + } + + record CharsetTestSpec(boolean toolProvider, CommandOutputControlSpec cocSpec) { + + void test() throws IOException, InterruptedException { + if (cocSpec.outputControl().stream().noneMatch(DumpOutputSink.class::isInstance)) { + throw new IllegalArgumentException(); + } + + final var expectedString = "veni-vidi-vici"; + + 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()); + exec = coc.createExecutable(tp); + } else { + var cmdline = Command.createShellCommandLine(Stream.of(expectedString).map(str -> { + return (str + System.lineSeparator()).getBytes(coc.charset()); + }).mapMulti((bytes, sink) -> { + sink.accept(CommandAction.writeStdout(bytes)); + sink.accept(CommandAction.writeStderr(bytes)); + }).toList()); + exec = coc.createExecutable(new ProcessBuilder(cmdline)); + } + + exec.execute(); + + for (var outputContolMutator : cocSpec.outputControl()) { + if (outputContolMutator instanceof DumpOutputSink sink) { + var actual = sink.lines(); + List expected; + if (cocSpec.redirectStderr()) { + switch (sink.streams()) { + case STDERR -> { + expected = List.of(); + } + default -> { + expected = List.of(expectedString, expectedString); + } + } + } else { + expected = List.of(expectedString); + } + assertEquals(expected, actual); + } + } + + } + + record DumpOutputSink(Charset charset, ByteArrayOutputStream buffer, OutputStreams streams) implements CommandOutputControlMutator { + DumpOutputSink { + Objects.requireNonNull(charset); + Objects.requireNonNull(buffer); + Objects.requireNonNull(streams); + } + + DumpOutputSink(Charset charset, OutputStreams streams) { + this(charset, new ByteArrayOutputStream(), streams); + } + + List lines() { + var str = buffer.toString(charset); + return new BufferedReader(new StringReader(str)).lines().toList(); + } + + @Override + public String name() { + return String.format("DUMP-%s-%s", streams, charset.name()); + } + + @Override + public void mutate(CommandOutputControl coc) { + var ps = new PrintStream(buffer, false, charset); + switch (streams) { + case STDOUT -> { + coc.dumpStdout(ps); + } + case STDERR -> { + coc.dumpStderr(ps); + } + case STDOUT_AND_STDERR -> { + // Easy to implement, but not used. + throw new IllegalArgumentException(); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + } + } + + private static final class InterruptibleToolProvider implements ToolProvider { + + InterruptibleToolProvider(ToolProvider impl) { + this.impl = impl; + } + + @Override + public String name() { + return impl.name(); + } + + @Override + public int run(PrintStream out, PrintStream err, String... args) { + return run(_ -> { + return impl.run(out, err, args); + }, args); + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + return run(_ -> { + return impl.run(out, err, args); + }, args); + } + + boolean interrupted() { + return interrupted.join(); + } + + private int run(Function workload, String... args) { + boolean interruptedValue = false; + try { + return workload.apply(args); + } catch (ExceptionBox ex) { + if (ex.getCause() instanceof InterruptedException) { + interruptedValue = true; + return 1; + } else { + throw ex; + } + } finally { + interrupted.complete(interruptedValue); + } + } + + private final ToolProvider impl; + private final CompletableFuture interrupted = new CompletableFuture<>(); + } + + private static List toStringList(byte[] data, Charset charset) { + try (var bufReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), charset))) { + return bufReader.lines().toList(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static List> withAndWithout (T value) { + return List.of(Optional.empty(), Optional.of(value)); + } + + private static final List BOOLEAN_VALUES = List.of(true, false); +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java new file mode 100644 index 00000000000..dec0c5d6b0a --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java @@ -0,0 +1,168 @@ +/* + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CommandOutputControlTestUtils { + + @ParameterizedTest + @MethodSource + public void test_isInterleave(TestSpec test) { + test.run(); + } + + private static Stream test_isInterleave() { + var data = new ArrayList(); + + data.addAll(List.of( + interleaved("Toaday", "Today", "a"), + interleaved("Todanaay", "Today", "ana"), + interleaved("aaaababaaa", "aaaba", "aabaa"), + interleaved("xxxxxxxxxxxyxxyx", "xxxxxxxy", "xxxxxyxx"), + interleaved("xyxxxxyxxxxxxxxx", "yxxxxxxx", "xxxyxxxx"), + interleaved("xxxxxxxyxxxxyxxx", "xxxyxxxx", "xxxxxxyx"), + interleaved("cbdddcdaadacdbddbdcdddccdabbadba", "cdddaaddbcdcdbab", "bdcadcbddddcabda"), + interleaved("ddbdcacddddbddbdbddadcaaccdcabab", "dbccdddbbddacdaa", "ddaddbdddacaccbb"), + interleaved("adccbacbacaacddadddcdbbddbbddddd", "acbcaacddddbdbdd", "dcabcadadcbdbddd"), + interleaved("abdbdabdaacdcdbddddadbbccddcddac", "addbaccbdddbcdda", "bbadaddddabcdcdc"), + interleaved("cdaacbddaabdddbddbddbddadbacccdc", "dabdadddbddabccc", "cacdabdbddbddacd"), + notInterleaved("Toady", "Today", "a"), + notInterleaved("", "Today", "a") + )); + + data.addAll(generateTestData("abcdefghijklmnopqrstuvwxyz", 10)); + data.addAll(generateTestData("xxxxxxxy", 8)); + data.addAll(generateTestData("aaabbbcccddddddd", 50)); + + return data.stream().flatMap(test -> { + return Stream.of(test, test.flip()); + }); + } + + private static List generateTestData(String src, int iteration) { + + var srcCodePoints = new ArrayList(); + src.codePoints().mapToObj(Integer::valueOf).forEach(srcCodePoints::add); + + var data = new ArrayList(); + + Function, String> toString = codePoints -> { + var arr = codePoints.stream().mapToInt(Integer::intValue).toArray(); + return new String(arr, 0, arr.length); + }; + + for (int i = 0; i < 10; i++) { + Collections.shuffle(srcCodePoints); + var a = List.copyOf(srcCodePoints); + + Collections.shuffle(srcCodePoints); + var b = List.copyOf(srcCodePoints); + + var zip = new int[srcCodePoints.size() * 2]; + for (int codePointIdx = 0; codePointIdx != a.size(); codePointIdx++) { + var dstIdx = codePointIdx * 2; + zip[dstIdx] = a.get(codePointIdx); + zip[dstIdx + 1] = b.get(codePointIdx); + } + + data.add(interleaved(toString.apply(Arrays.stream(zip).boxed().toList()), toString.apply(a), toString.apply(b))); + } + + return data; + } + + public record TestSpec(String combined, String a, String b, boolean expected) { + + public TestSpec { + Objects.requireNonNull(combined); + Objects.requireNonNull(a); + Objects.requireNonNull(b); + } + + TestSpec flip() { + return new TestSpec(combined, b, a, expected); + } + + void run() { + assertEquals(expected, isInterleave( + combined.chars().mapToObj(Integer::valueOf).toList(), + a.chars().mapToObj(Integer::valueOf).toList(), + b.chars().mapToObj(Integer::valueOf).toList()), + String.format("combined: %s; a=%s; b=%s", combined, a, b)); + } + } + + private static TestSpec interleaved(String combined, String a, String b) { + return new TestSpec(combined, a, b, true); + } + + private static TestSpec notInterleaved(String combined, String a, String b) { + return new TestSpec(combined, a, b, false); + } + + // Solves the standard "Find if a string C is an interleave of strings A and B." + // problem but use containers instead of strings. + static boolean isInterleave(List combined, List a, List b) { + + if (a.size() + b.size() != combined.size()) { + return false; + } + + final var n = a.size(); + final var m = b.size(); + + var prev = new boolean[m + 1]; + final var cur = new boolean[m + 1]; + + prev[0] = true; + + for (int j = 1; j <= m; j++) { + prev[j] = prev[j - 1] && Objects.equals(b.get(j - 1), combined.get(j - 1)); + } + + for (int i = 1; i <= n; i++) { + cur[0] = prev[0] && Objects.equals(a.get(i - 1), combined.get(i - 1)); + + for (int j = 1; j <= m; j++) { + int k = i + j; + cur[j] = (prev[j] && Objects.equals(a.get(i - 1), combined.get(k - 1))) + || (cur[j - 1] && Objects.equals(b.get(j - 1), combined.get(k - 1))); + } + + prev = cur.clone(); + } + + return prev[m]; + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/EnquoterTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/EnquoterTest.java similarity index 57% rename from test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/EnquoterTest.java rename to test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/EnquoterTest.java index 95b0e54a0cd..8dc44f3f2fd 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/EnquoterTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/EnquoterTest.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 @@ -21,11 +21,13 @@ * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -45,27 +47,41 @@ public class EnquoterTest { assertEquals(expected, actual); } - private static Stream testForShellLiterals() { + @ParameterizedTest + @MethodSource + public void testIdentity(String input) { + var actual = Enquoter.identity().applyTo(input); + assertEquals(input, actual); + } + + @ParameterizedTest + @MethodSource("testIdentity") + public void testNoEscaper(String input) { + var actual = Enquoter.identity().setEnquotePredicate(_ -> true).applyTo(input); + assertEquals('"' + input + '"', actual); + } + + private static Stream testForShellLiterals() { return Stream.of( - makeArguments("''", ""), - makeArguments("'foo'", "foo"), - makeArguments("' foo '", " foo "), - makeArguments("'foo bar'", "foo bar"), - makeArguments("'foo\\' bar'", "foo' bar") + Arguments.of("''", ""), + Arguments.of("'foo'", "foo"), + Arguments.of("' foo '", " foo "), + Arguments.of("'foo bar'", "foo bar"), + Arguments.of("'foo\\' bar'", "foo' bar") ); } - private static Stream testForPropertyValues() { + private static Stream testForPropertyValues() { return Stream.of( - makeArguments("", ""), - makeArguments("foo", "foo"), - makeArguments("\" foo \"", " foo "), - makeArguments("\"foo bar\"", "foo bar"), - makeArguments("\"foo' bar\"", "foo' bar") + Arguments.of("", ""), + Arguments.of("foo", "foo"), + Arguments.of("\" foo \"", " foo "), + Arguments.of("\"foo bar\"", "foo bar"), + Arguments.of("\"foo' bar\"", "foo' bar") ); } - static org.junit.jupiter.params.provider.Arguments makeArguments(Object ... args) { - return org.junit.jupiter.params.provider.Arguments.of(args); + private static Stream testIdentity() { + return Stream.of("", "foo", " foo ", "foo bar", "foo' bar"); } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java new file mode 100644 index 00000000000..abb0363da80 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java @@ -0,0 +1,331 @@ +/* + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.time.Duration; +import java.util.Objects; +import jdk.jpackage.internal.util.RetryExecutor.Context; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class RetryExecutorTest { + + @Test + public void test_defaults() { + + var executor = new AttemptCounter(context -> { + throw new AttemptFailedException(); + }); + + var defaultTimeout = Duration.ofSeconds(2); + var defaultAttemptCount = 5; + + var timeout = Slot.createEmpty(); + + assertThrowsExactly(AttemptFailedException.class, new RetryExecutor(Exception.class) + .setExecutable(executor) + .setSleepFunction(t -> { + assertEquals(defaultTimeout, t); + timeout.set(t); + return; + })::execute); + + assertEquals(defaultTimeout, timeout.get()); + assertEquals(defaultAttemptCount, executor.count()); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2, 3, -4}) + public void test_N_attempts_fail(int maxAttemptsCount) throws AttemptFailedException { + + var retry = new RetryExecutor(AttemptFailedException.class) + .setMaxAttemptsCount(maxAttemptsCount) + .setAttemptTimeout(null) + .setExecutable(context -> { + if (context.attempt() == (maxAttemptsCount - 1)) { + assertTrue(context.isLastAttempt()); + } else { + assertFalse(context.isLastAttempt()); + } + throw new AttemptFailedException("Attempt: " + context.attempt()); + }); + + if (maxAttemptsCount <= 0) { + assertNull(retry.execute()); + } else { + var ex = assertThrowsExactly(AttemptFailedException.class, retry::execute); + assertEquals("Attempt: " + (maxAttemptsCount - 1), ex.getMessage()); + } + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + public void test_N_attempts_last_succeed(int maxAttemptsCount) throws AttemptFailedException { + test_N_attempts_M_succeed(maxAttemptsCount, maxAttemptsCount - 1, false); + } + + @ParameterizedTest + @ValueSource(ints = {2, 3}) + public void test_N_attempts_first_succeed(int maxAttemptsCount) throws AttemptFailedException { + test_N_attempts_M_succeed(maxAttemptsCount, 0, false); + } + + @Test + public void test_N_attempts_2nd_succeed() throws AttemptFailedException { + test_N_attempts_M_succeed(4, 1, false); + } + + @Test + public void test_N_attempts_2nd_succeed_unchecked() throws AttemptFailedException { + test_N_attempts_M_succeed(4, 1, true); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_null_executor(boolean dynamic) { + var retry = new RetryExecutor(AttemptFailedException.class) + .setAttemptTimeout(null).setMaxAttemptsCount(1000); + + if (dynamic) { + int maxAttemptsCount = 3; + var executor = new AttemptCounter(context -> { + assertTrue(context.attempt() <= (maxAttemptsCount - 1)); + if (context.attempt() == (maxAttemptsCount - 1)) { + context.executor().setExecutable((ThrowingSupplier)null); + } + throw new AttemptFailedException("foo"); + }); + + retry.setExecutable(executor); + + var ex = assertThrowsExactly(IllegalStateException.class, retry::execute); + assertEquals("No executable", ex.getMessage()); + assertEquals(3, executor.count()); + } else { + var ex = assertThrowsExactly(IllegalStateException.class, retry::execute); + assertEquals("No executable", ex.getMessage()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_unexpected_exception(boolean executeUnchecked) { + var cause = new UnsupportedOperationException("foo"); + + var executor = new AttemptCounter(context -> { + assertEquals(0, context.attempt()); + throw cause; + }); + + var retry = new RetryExecutor(IOException.class).setExecutable(executor) + .setMaxAttemptsCount(10).setAttemptTimeout(null); + + UnsupportedOperationException ex; + if (executeUnchecked) { + ex = assertThrowsExactly(UnsupportedOperationException.class, retry::executeUnchecked); + } else { + ex = assertThrowsExactly(UnsupportedOperationException.class, retry::execute); + } + assertSame(cause, ex); + assertEquals(1, executor.count()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_dynamic(boolean abort) { + int maxAttemptsCount = 4; + + var secondExecutor = new AttemptCounter(context -> { + throw new AttemptFailedException("bar"); + }); + + var firstExecutor = new AttemptCounter(context -> { + assertTrue(context.attempt() <= (maxAttemptsCount - 1)); + if (context.attempt() == (maxAttemptsCount - 1)) { + if (abort) { + context.executor().setMaxAttemptsCount(maxAttemptsCount); + } else { + // Let it go two more times. + context.executor().setMaxAttemptsCount(maxAttemptsCount + 2); + } + context.executor().setExecutable(secondExecutor); + } + throw new AttemptFailedException("foo"); + }); + + var retry = new RetryExecutor(AttemptFailedException.class) + .setExecutable(firstExecutor) + .setMaxAttemptsCount(1000000) + .setAttemptTimeout(null); + + var ex = assertThrowsExactly(AttemptFailedException.class, retry::execute); + if (abort) { + assertEquals("foo", ex.getMessage()); + assertEquals(0, secondExecutor.count()); + } else { + assertEquals("bar", ex.getMessage()); + assertEquals(2, secondExecutor.count()); + } + assertEquals(maxAttemptsCount, firstExecutor.count()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_supplier_executor(boolean isNull) throws Exception { + var retry = new RetryExecutor(Exception.class).setMaxAttemptsCount(1); + if (isNull) { + retry.setExecutable((ThrowingSupplier)null); + var ex = assertThrowsExactly(IllegalStateException.class, retry::execute); + assertEquals("No executable", ex.getMessage()); + } else { + retry.setExecutable(() -> "Hello"); + assertEquals("Hello", retry.execute()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_executeUnchecked_fail(boolean withExceptionMapper) throws AttemptFailedException { + var retry = new RetryExecutor(AttemptFailedException.class).setExecutable(() -> { + throw new AttemptFailedException("kaput!"); + }).setMaxAttemptsCount(1); + + Class expectedExceptionType; + if (withExceptionMapper) { + retry.setExceptionMapper((AttemptFailedException ex) -> { + assertEquals("kaput!", ex.getMessage()); + return new UncheckedAttemptFailedException(ex); + }); + expectedExceptionType = UncheckedAttemptFailedException.class; + } else { + expectedExceptionType = ExceptionBox.class; + } + + var ex = assertThrowsExactly(expectedExceptionType, retry::executeUnchecked); + assertEquals(AttemptFailedException.class, ex.getCause().getClass()); + assertEquals("kaput!", ex.getCause().getMessage()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_setSleepFunction(boolean withTimeout) { + + var timeout = Slot.createEmpty(); + + assertDoesNotThrow(new RetryExecutor(AttemptFailedException.class) + .setMaxAttemptsCount(2) + .mutate(retry -> { + if (withTimeout) { + retry.setAttemptTimeout(Duration.ofDays(100)); + } else { + retry.setAttemptTimeout(null); + } + }) + .setExecutable(context -> { + if (context.isLastAttempt()) { + return null; + } else { + throw new AttemptFailedException(); + } + }) + .setSleepFunction(timeout::set)::execute); + + assertEquals(withTimeout, timeout.find().isPresent()); + if (withTimeout) { + assertEquals(Duration.ofDays(100), timeout.get()); + } + } + + private static void test_N_attempts_M_succeed(int maxAttempts, int failedAttempts, boolean unchecked) throws AttemptFailedException { + + var countingExecutor = new AttemptCounter(context -> { + if (context.attempt() == failedAttempts) { + return "You made it!"; + } else { + throw new AttemptFailedException(); + } + }); + + var retry = new RetryExecutor(AttemptFailedException.class) + .setMaxAttemptsCount(maxAttempts) + .setAttemptTimeout(null) + .setExecutable(countingExecutor); + + assertEquals("You made it!", unchecked ? retry.execute() : retry.executeUnchecked()); + assertEquals(failedAttempts, countingExecutor.count() - 1); + } + + private static final class AttemptCounter implements ThrowingFunction>, T, E> { + + AttemptCounter(ThrowingFunction>, T, E> impl) { + this.impl = Objects.requireNonNull(impl); + } + + @Override + public T apply(Context> context) throws E { + counter++; + return impl.apply(context); + } + + int count() { + return counter; + } + + private int counter; + private final ThrowingFunction>, T, E> impl; + } + + private static final class AttemptFailedException extends Exception { + + AttemptFailedException(String msg) { + super(msg); + } + + AttemptFailedException() { + } + + private static final long serialVersionUID = 1L; + } + + private static final class UncheckedAttemptFailedException extends RuntimeException { + + UncheckedAttemptFailedException(AttemptFailedException ex) { + super(ex); + } + + private static final long serialVersionUID = 1L; + } +} diff --git a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java index 952b4b520d5..4ab2c89873c 100644 --- a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.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 @@ -29,11 +29,11 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; +import jdk.jpackage.internal.util.TeeOutputStream; import jdk.jpackage.internal.util.function.ThrowingRunnable; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; @@ -70,12 +70,16 @@ public class JUnitAdapter { static List captureJPackageTestLog(ThrowingRunnable runnable) { final var buf = new ByteArrayOutputStream(); - try (PrintStream ps = new PrintStream(buf, true, StandardCharsets.UTF_8)) { - TKit.withExtraLogStream(runnable, ps); - } + var ps = new PrintStream(buf, false, TKit.state().out().charset()); + + final var out = new PrintStream(new TeeOutputStream(List.of(TKit.state().out(), ps)), true, ps.charset()); + + TKit.withOutput(runnable, out, TKit.state().err()); + + ps.flush(); try (final var in = new ByteArrayInputStream(buf.toByteArray()); - final var reader = new InputStreamReader(in, StandardCharsets.UTF_8); + final var reader = new InputStreamReader(in, ps.charset()); final var bufReader = new BufferedReader(reader)) { return bufReader.lines().map(line -> { // Skip timestamp diff --git a/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java b/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java index 1a42a30c00e..e4adf3b9616 100644 --- a/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java +++ b/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.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 @@ -49,11 +49,10 @@ public class ArgumentsFilteringTest { public void test1() { JPackageCommand cmd = JPackageCommand.helloAppImage(); cmd.executeAndAssertHelloAppImageCreated(); - var appVerifier = HelloApp.assertMainLauncher(cmd); - if (appVerifier != null) { + HelloApp.assertMainLauncher(cmd).ifPresent(appVerifier -> { appVerifier.execute("-psn_1_1"); appVerifier.verifyOutput(); - } + }); } @Test @@ -61,10 +60,9 @@ public class ArgumentsFilteringTest { JPackageCommand cmd = JPackageCommand.helloAppImage() .addArguments("--arguments", "-psn_2_2"); cmd.executeAndAssertHelloAppImageCreated(); - var appVerifier = HelloApp.assertMainLauncher(cmd); - if (appVerifier != null) { + HelloApp.assertMainLauncher(cmd).ifPresent(appVerifier -> { appVerifier.execute("-psn_1_1"); appVerifier.verifyOutput("-psn_2_2"); - } + }); } } diff --git a/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java b/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java index 7498043c14f..7aebff7cb00 100644 --- a/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java +++ b/test/jdk/tools/jpackage/macosx/HostArchPkgTest.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 @@ -64,8 +64,7 @@ public class HostArchPkgTest { dbf.setFeature("http://apache.org/xml/features/" + "nonvalidating/load-external-dtd", false); DocumentBuilder b = dbf.newDocumentBuilder(); - org.w3c.dom.Document doc - = b.parse(Files.newInputStream(distributionFile)); + org.w3c.dom.Document doc = b.parse(distributionFile.toFile()); XPath xPath = XPathFactory.newInstance().newXPath(); diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index bfd731b67d5..ce2300c92d1 100644 --- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java +++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.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 @@ -26,6 +26,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.function.Predicate; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; @@ -133,8 +134,7 @@ public class AppImagePackageTest { */ @Test public static void testBadAppImage() throws IOException { - Path appImageDir = TKit.createTempDirectory("appimage"); - Files.createFile(appImageDir.resolve("foo")); + Path appImageDir = createInvalidAppImage(); configureBadAppImage(appImageDir).addInitializer(cmd -> { cmd.removeArgumentWithValue("--name"); }).run(Action.CREATE); @@ -145,8 +145,7 @@ public class AppImagePackageTest { */ @Test public static void testBadAppImage2() throws IOException { - Path appImageDir = TKit.createTempDirectory("appimage"); - Files.createFile(appImageDir.resolve("foo")); + Path appImageDir = createInvalidAppImage(); configureBadAppImage(appImageDir).run(Action.CREATE); } @@ -227,4 +226,19 @@ public class AppImagePackageTest { + TKit.ICON_SUFFIX)); } + private static Path createInvalidAppImage() throws IOException { + Path appImageDir = TKit.createTempDirectory("appimage"); + if (TKit.isOSX()) { + // Create minimal macOS bundle to prevent jpackage bail out early + // with "error.parameter-not-mac-bundle" error. + var bundle = new MacBundle(appImageDir); + Files.createDirectories(bundle.macOsDir()); + Files.createFile(bundle.infoPlistFile()); + } else { + Files.createFile(appImageDir.resolve("foo")); + } + + return appImageDir; + } + } diff --git a/test/jdk/tools/jpackage/share/AsyncTest.java b/test/jdk/tools/jpackage/share/AsyncTest.java index e3a3f7e3cb8..b7dd13f91fa 100644 --- a/test/jdk/tools/jpackage/share/AsyncTest.java +++ b/test/jdk/tools/jpackage/share/AsyncTest.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 @@ -23,7 +23,6 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.Collection; @@ -34,15 +33,12 @@ import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -import java.util.spi.ToolProvider; import java.util.stream.IntStream; -import jdk.jpackage.internal.util.function.ThrowingRunnable; import jdk.jpackage.internal.util.Slot; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.Main; import jdk.jpackage.test.PackageTest; @@ -66,10 +62,10 @@ public class AsyncTest { // Create test jar only once. // Besides of saving time, this avoids asynchronous invocations of java tool provider that randomly fail. - APP_JAR.set(HelloApp.createBundle(JavaAppDesc.parse("Hello!"), TKit.workDir())); + var appJar = HelloApp.createBundle(JavaAppDesc.parse("Hello!"), TKit.workDir()); // - // Run test cases from InternalAsyncTest class asynchronously. + // Run test cases from AsyncInnerTest class asynchronously. // Spawn a thread for every test case. // Input data for test cases will be cooked asynchronously but in a safe way because every test case has an isolated work directory. // Multiple jpackage tool provider instances will be invoked asynchronously. @@ -79,14 +75,10 @@ public class AsyncTest { var testFuncNames = List.of("testAppImage", "testNativeBundle"); - var runArg = String.format("--jpt-run=%s", AsyncInnerTest.class.getName()); - var futures = executor.invokeAll(IntStream.range(0, JOB_COUNT).mapToObj(Integer::toString).mapMulti((idx, consumer) -> { for (var testFuncName : testFuncNames) { var id = String.format("%s(%s)", testFuncName, idx); - consumer.accept(new Workload(() -> { - Main.main(runArg, String.format("--jpt-include=%s", id)); - }, id)); + consumer.accept(new Workload(id, appJar)); } }).toList()); @@ -99,10 +91,8 @@ public class AsyncTest { for (var future : futures) { var result = future.get(); - TKit.trace(String.format("[%s] STDOUT BEGIN\n%s", result.id(), result.stdoutBuffer())); - TKit.trace(String.format("[%s] STDOUT END", result.id())); - TKit.trace(String.format("[%s] STDERR BEGIN\n%s", result.id(), result.stderrBuffer())); - TKit.trace(String.format("[%s] STDERR END", result.id())); + TKit.trace(String.format("[%s] OUTPUT BEGIN\n%s", result.testCaseId(), result.testOutput())); + TKit.trace(String.format("[%s] OUTPUT END", result.testCaseId())); result.exception().filter(Predicate.not(TKit::isSkippedException)).ifPresent(fatalError::set); } @@ -142,80 +132,56 @@ public class AsyncTest { } - private record Result(String stdoutBuffer, String stderrBuffer, String id, Optional exception) { + private record Result(String testOutput, String testCaseId, Optional exception) { Result { - Objects.requireNonNull(stdoutBuffer); - Objects.requireNonNull(stderrBuffer); - Objects.requireNonNull(id); + Objects.requireNonNull(testOutput); + Objects.requireNonNull(testCaseId); Objects.requireNonNull(exception); } } private record Workload( - ByteArrayOutputStream stdoutBuffer, - ByteArrayOutputStream stderrBuffer, - ThrowingRunnable runnable, - String id) implements Callable { + String testCaseId, + ByteArrayOutputStream outputSink, + Path appJar) implements Callable { Workload { - Objects.requireNonNull(stdoutBuffer); - Objects.requireNonNull(stderrBuffer); - Objects.requireNonNull(runnable); - Objects.requireNonNull(id); + Objects.requireNonNull(testCaseId); + Objects.requireNonNull(outputSink); + Objects.requireNonNull(appJar); } - Workload(ThrowingRunnable runnable, String id) { - this(new ByteArrayOutputStream(), new ByteArrayOutputStream(), runnable, id); + Workload(String testCaseId, Path appJar) { + this(testCaseId, new ByteArrayOutputStream(), appJar); } - private String stdoutBufferAsString() { - return new String(stdoutBuffer.toByteArray(), StandardCharsets.UTF_8); - } - - private String stderrBufferAsString() { - return new String(stderrBuffer.toByteArray(), StandardCharsets.UTF_8); + private String testOutput() { + return new String(outputSink.toByteArray(), StandardCharsets.UTF_8); } @Override public Result call() { - // Reset the current test inherited in the state from the parent thread. - TKit.state(DEFAULT_STATE); - - var defaultToolProvider = JavaTool.JPACKAGE.asToolProvider(); - - JPackageCommand.useToolProviderByDefault(new ToolProvider() { - - @Override - public int run(PrintWriter out, PrintWriter err, String... args) { - try (var bufOut = new PrintWriter(stdoutBuffer, true, StandardCharsets.UTF_8); - var bufErr = new PrintWriter(stderrBuffer, true, StandardCharsets.UTF_8)) { - return defaultToolProvider.run(bufOut, bufErr, args); - } - } - - @Override - public String name() { - return defaultToolProvider.name(); - } - }); + var runArg = String.format("--jpt-run=%s", AsyncInnerTest.class.getName()); Optional err = Optional.empty(); - try (var bufOut = new PrintStream(stdoutBuffer, true, StandardCharsets.UTF_8); - var bufErr = new PrintStream(stderrBuffer, true, StandardCharsets.UTF_8)) { - TKit.withStackTraceStream(() -> { - TKit.withMainLogStream(runnable, bufOut); - }, bufErr); + try { + try (var out = new PrintStream(outputSink, false, System.out.charset())) { + ScopedValue.where(APP_JAR, appJar).run(() -> { + TKit.withOutput(() -> { + JPackageCommand.useToolProviderByDefault(); + Main.main("--jpt-ignore-logfile", runArg, String.format("--jpt-include=%s", testCaseId)); + }, out, out); + }); + } } catch (Exception ex) { err = Optional.of(ex); } - return new Result(stdoutBufferAsString(), stderrBufferAsString(), id, err); + return new Result(testOutput(), testCaseId, err); } } - private static final int JOB_COUNT = 30; - private static final TKit.State DEFAULT_STATE = TKit.state(); - private static final InheritableThreadLocal APP_JAR = new InheritableThreadLocal<>(); + private static final ScopedValue APP_JAR = ScopedValue.newInstance(); } diff --git a/test/jdk/tools/jpackage/share/BasicTest.java b/test/jdk/tools/jpackage/share/BasicTest.java index afa847f6e1b..977b7c7c057 100644 --- a/test/jdk/tools/jpackage/share/BasicTest.java +++ b/test/jdk/tools/jpackage/share/BasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ +import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE; import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK; import java.io.IOException; @@ -32,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -41,6 +43,7 @@ import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.Executor; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; @@ -176,57 +179,74 @@ public final class BasicTest { } @Test - @SuppressWarnings("unchecked") - public void testVerbose() { - JPackageCommand cmd = JPackageCommand.helloAppImage() - // Disable default logic adding `--verbose` option - // to jpackage command line. - .ignoreDefaultVerbose(true) - .saveConsoleOutput(true) - .setFakeRuntime().executePrerequisiteActions(); + @Parameter("false") + @Parameter("true") + public void testQuiet(boolean appImage) { - List expectedVerboseOutputStrings = new ArrayList<>(); - expectedVerboseOutputStrings.add("Creating app package:"); - if (TKit.isWindows()) { - expectedVerboseOutputStrings.add( - "Succeeded in building Windows Application Image package"); - } else if (TKit.isLinux()) { - expectedVerboseOutputStrings.add( - "Succeeded in building Linux Application Image package"); - } else if (TKit.isOSX()) { - expectedVerboseOutputStrings.add("Preparing Info.plist:"); - expectedVerboseOutputStrings.add( - "Succeeded in building Mac Application Image package"); + ConfigurationTarget target; + if (appImage) { + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); } else { - TKit.throwUnknownPlatformError(); + target = new ConfigurationTarget(new PackageTest().configureHelloApp()); } - TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); - List nonVerboseOutput = cmd.execute().getOutput(); - List[] verboseOutput = (List[])new List[1]; - - // Directory clean up is not 100% reliable on Windows because of - // antivirus software that can lock .exe files. Setup - // different output directory instead of cleaning the default one for - // verbose jpackage run. - TKit.withTempDirectory("verbose-output", tempDir -> { - cmd.setArgumentValue("--dest", tempDir); - cmd.addArgument("--verbose"); - verboseOutput[0] = cmd.execute().getOutput(); + target.addInitializer(cmd -> { + // Disable the default logic adding `--verbose` option to jpackage command line. + cmd.ignoreDefaultVerbose(true) + .useToolProvider(true) + .saveConsoleOutput(true) + .setFakeRuntime(); }); - TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), - "Check verbose output is longer than regular"); + Consumer asserter = result -> { + TKit.assertStringListEquals(List.of(), result.getOutput(), "Check output is empty"); + }; - expectedVerboseOutputStrings.forEach(str -> { - TKit.assertTextStream(str).label("regular output") - .predicate(String::contains).negate() - .apply(nonVerboseOutput); + target.cmd().map(JPackageCommand::execute).ifPresent(asserter); + target.test().ifPresent(test -> { + test.addBundleVerifier((_, result) -> { + asserter.accept(result); + }).run(CREATE); + }); + } + + @Test + @Parameter("false") + @Parameter("true") + public void testVerbose(boolean appImage) { + + ConfigurationTarget target; + if (appImage) { + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); + } else { + target = new ConfigurationTarget(new PackageTest().configureHelloApp()); + } + + target.addInitializer(cmd -> { + // Disable the default logic adding `--verbose` option to jpackage command line. + cmd.ignoreDefaultVerbose(true) + .useToolProvider(true) + .addArgument("--verbose") + .saveConsoleOutput(true) + .setFakeRuntime(); + + List verboseContent; + if (appImage) { + verboseContent = List.of( + JPackageStringBundle.MAIN.cannedFormattedString("message.create-app-image"), + JPackageStringBundle.MAIN.cannedFormattedString("message.app-image-created")); + } else { + verboseContent = List.of( + JPackageStringBundle.MAIN.cannedFormattedString("message.create-package"), + JPackageStringBundle.MAIN.cannedFormattedString("message.package-created")); + } + + cmd.validateOutput(verboseContent.toArray(CannedFormattedString[]::new)); }); - expectedVerboseOutputStrings.forEach(str -> { - TKit.assertTextStream(str).label("verbose output") - .apply(verboseOutput[0]); + target.cmd().ifPresent(JPackageCommand::execute); + target.test().ifPresent(test -> { + test.run(CREATE); }); } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index bc12577fa20..f31ac42dea0 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -441,10 +441,7 @@ public final class ErrorTest { .error("error.no-module-in-path", "com.foo.bar"), // non-existing argument file testSpec().noAppDesc().notype().addArgs("@foo") - .error("ERR_CannotParseOptions", "foo"), - // invalid jlink option - testSpec().addArgs("--jlink-options", "--foo") - .error("error.jlink.failed", "Error: unknown option: --foo") + .error("ERR_CannotParseOptions", "foo") ).map(TestSpec.Builder::create).toList()); // --main-jar and --module-name @@ -646,7 +643,12 @@ public final class ErrorTest { .error("message.invalid-identifier", "#1"), // Bundle for mac app store should not have runtime commands testSpec().nativeType().addArgs("--mac-app-store", "--jlink-options", "--bind-services") - .error("ERR_MissingJLinkOptMacAppStore", "--strip-native-commands") + .error("ERR_MissingJLinkOptMacAppStore", "--strip-native-commands"), + // Predefined app image must be a valid macOS bundle. + testSpec().noAppDesc().nativeType().addArgs("--app-image", Token.EMPTY_DIR.token()) + .error("error.parameter-not-mac-bundle", JPackageCommand.cannedArgument(cmd -> { + return Path.of(cmd.getArgumentValue("--app-image")); + }, Token.EMPTY_DIR.token()), "--app-image") ).map(TestSpec.Builder::create).toList()); macInvalidRuntime(testCases::add); diff --git a/test/jdk/tools/jpackage/share/MainClassTest.java b/test/jdk/tools/jpackage/share/MainClassTest.java index bc813c4ec15..72e77bbbff5 100644 --- a/test/jdk/tools/jpackage/share/MainClassTest.java +++ b/test/jdk/tools/jpackage/share/MainClassTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -240,8 +240,7 @@ public final class MainClassTest { cmd.executeAndAssertHelloAppImageCreated(); } else { cmd.executeAndAssertImageCreated(); - var appVerifier = HelloApp.assertMainLauncher(cmd); - if (appVerifier != null) { + HelloApp.assertMainLauncher(cmd).ifPresent(appVerifier -> { List output = appVerifier .saveOutput(true) .expectedExitCode(1) @@ -249,7 +248,7 @@ public final class MainClassTest { TKit.assertTextStream(String.format( "Error: Could not find or load main class %s", nonExistingMainClass)).apply(output); - } + }); } CfgFile cfg = cmd.readLauncherCfgFile(); diff --git a/test/jdk/tools/jpackage/share/OutputErrorTest.java b/test/jdk/tools/jpackage/share/OutputErrorTest.java new file mode 100644 index 00000000000..110e86e67d9 --- /dev/null +++ b/test/jdk/tools/jpackage/share/OutputErrorTest.java @@ -0,0 +1,119 @@ +/* + * 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.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.spi.ToolProvider; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.JavaTool; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.TKit; + +/* + * @test + * @summary Test how jpackage handles errors writing output bundle + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror OutputErrorTest.java + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=OutputErrorTest + */ + +public final class OutputErrorTest { + + @Test + @Parameter("DIR") + // "Locked file error" reliably works only on Windows + @Parameter(value = "LOCKED_FILE", ifOS = OperatingSystem.WINDOWS) + public void testPackage(ExistingOutputBundleType existingOutputBundleType) { + + new PackageTest().configureHelloApp().addInitializer(cmd -> { + + cmd.setFakeRuntime(); + cmd.setArgumentValue("--dest", TKit.createTempDirectory("output")); + cmd.removeOldOutputBundle(false); + cmd.validateOutput(JPackageCommand.makeError(JPackageStringBundle.MAIN.cannedFormattedString( + "error.output-bundle-cannot-be-overwritten", cmd.outputBundle().toAbsolutePath()))); + + var outputBundle = cmd.outputBundle(); + + switch (existingOutputBundleType) { + case DIR -> { + Files.createDirectories(outputBundle); + Files.writeString(outputBundle.resolve("foo.txt"), "Hello"); + } + case LOCKED_FILE -> { + Files.writeString(outputBundle, "Hello"); + cmd.useToolProvider(createToolProviderWithLockedFile( + JavaTool.JPACKAGE.asToolProvider(), outputBundle)); + } + } + + }).setExpectedExitCode(1).run(); + } + + enum ExistingOutputBundleType { + DIR, + LOCKED_FILE, + ; + } + + private static ToolProvider createToolProviderWithLockedFile(ToolProvider tp, Path lockedFile) { + Objects.requireNonNull(tp); + if (!Files.isRegularFile(lockedFile)) { + throw new IllegalArgumentException(); + } + + return new ToolProvider() { + + @Override + public String name() { + return "jpackage-mock"; + } + + @SuppressWarnings("try") + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + try { + var lastModifiedTime = Files.getLastModifiedTime(lockedFile); + try (var fos = new FileOutputStream(lockedFile.toFile()); var lock = fos.getChannel().lock()) { + Files.setLastModifiedTime(lockedFile, lastModifiedTime); + return tp.run(out, err, args); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + }; + } +} diff --git a/test/jdk/tools/jpackage/share/PostImageScriptTest.java b/test/jdk/tools/jpackage/share/PostImageScriptTest.java index 655658f036d..81cdfffbf46 100644 --- a/test/jdk/tools/jpackage/share/PostImageScriptTest.java +++ b/test/jdk/tools/jpackage/share/PostImageScriptTest.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 @@ -229,7 +229,7 @@ public class PostImageScriptTest { cmd.saveConsoleOutput(true); }).addBundleVerifier((cmd, result) -> { - final var imageDir = result.stdout().getOutput().stream().map(String::stripLeading).filter(str -> { + final var imageDir = result.stdout().stream().map(String::stripLeading).filter(str -> { return str.startsWith(imageDirOutputPrefix); }).map(str -> { return str.substring(imageDirOutputPrefix.length()); diff --git a/test/jdk/tools/jpackage/windows/Win8301247Test.java b/test/jdk/tools/jpackage/windows/Win8301247Test.java index 2f98141dcb4..bed795281f4 100644 --- a/test/jdk/tools/jpackage/windows/Win8301247Test.java +++ b/test/jdk/tools/jpackage/windows/Win8301247Test.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 @@ -21,12 +21,14 @@ * questions. */ -import static jdk.jpackage.test.WindowsHelper.killAppLauncherProcess; - import java.time.Duration; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; /** * Test that terminating of the parent app launcher process automatically @@ -46,7 +48,7 @@ import jdk.jpackage.test.JPackageCommand; public class Win8301247Test { @Test - public void test() throws InterruptedException { + public void test() throws InterruptedException, ExecutionException { var cmd = JPackageCommand.helloAppImage().ignoreFakeRuntime(); // Launch the app in a way it doesn't exit to let us trap app laucnher @@ -54,20 +56,44 @@ public class Win8301247Test { cmd.addArguments("--java-options", "-Djpackage.test.noexit=true"); cmd.executeAndAssertImageCreated(); + var state = TKit.state(); + var f = new CompletableFuture(); + // Launch the app in a separate thread new Thread(() -> { - HelloApp.executeLauncher(cmd); + TKit.withState(() -> { + HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); + }, state); }).start(); - // Wait a bit to let the app start - Thread.sleep(Duration.ofSeconds(10)); + var mainLauncherProcess = f.get(); - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, 2); + Optional childProcess = Optional.empty(); - // Wait a bit and check if child app launcher process is still running (it must NOT) - Thread.sleep(Duration.ofSeconds(5)); + try { + // Wait a bit to let the app start + Thread.sleep(Duration.ofSeconds(10)); - killAppLauncherProcess(cmd, null, 0); + try (var children = mainLauncherProcess.children()) { + childProcess = children.filter(p -> { + return mainLauncherProcess.info().command().equals(p.info().command()); + }).findFirst(); + } + + TKit.assertTrue(childProcess.isPresent(), + String.format("Check the main launcher process with PID=%d restarted", mainLauncherProcess.pid())); + } finally { + // Kill the main app launcher process + TKit.trace("About to kill the main launcher process..."); + mainLauncherProcess.destroyForcibly(); + + // Wait a bit and check if child app launcher process is still running (it must NOT) + Thread.sleep(Duration.ofSeconds(5)); + + childProcess.ifPresent(p -> { + TKit.assertTrue(!p.isAlive(), String.format( + "Check restarted main launcher process with PID=%d is not alive", p.pid())); + }); + } } } diff --git a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java index a83ef837331..e5de19d182a 100644 --- a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java +++ b/test/jdk/tools/jpackage/windows/WinChildProcessTest.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 @@ -44,7 +44,6 @@ import static jdk.jpackage.test.HelloApp.configureAndExecute; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; import jdk.jpackage.test.TKit; -import static jdk.jpackage.test.WindowsHelper.killProcess; public class WinChildProcessTest { private static final Path TEST_APP_JAVA = TKit.TEST_SRC_ROOT @@ -52,7 +51,7 @@ public class WinChildProcessTest { @Test public static void test() { - long childPid = 0; + Optional child = Optional.empty(); try { JPackageCommand cmd = JPackageCommand .helloAppImage(TEST_APP_JAVA + "*Hello") @@ -69,21 +68,18 @@ public class WinChildProcessTest { String pidStr = output.get(0); // parse child PID - childPid = Long.parseLong(pidStr.split("=", 2)[1]); + var childPid = Long.parseLong(pidStr.split("=", 2)[1]); // Check whether the termination of third party application launcher // also terminating the launched third party application // If third party application is not terminated the test is // successful else failure - Optional processHandle = ProcessHandle.of(childPid); - boolean isAlive = processHandle.isPresent() - && processHandle.get().isAlive(); - TKit.assertTrue(isAlive, "Check child process is alive"); + child = ProcessHandle.of(childPid); + boolean isAlive = child.map(ProcessHandle::isAlive).orElse(false); + TKit.assertTrue(isAlive, String.format("Check child process with PID=%d is alive", childPid)); } finally { - if (childPid != 0) { - // Kill only a specific child instance - killProcess(childPid); - } + TKit.trace("About to kill the child process..."); + child.ifPresent(ProcessHandle::destroyForcibly); } } } diff --git a/test/jdk/tools/jpackage/windows/WinLongVersionTest.java b/test/jdk/tools/jpackage/windows/WinLongVersionTest.java index 2ee76bcaa15..13650b2dfd3 100644 --- a/test/jdk/tools/jpackage/windows/WinLongVersionTest.java +++ b/test/jdk/tools/jpackage/windows/WinLongVersionTest.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 @@ -180,9 +180,8 @@ public class WinLongVersionTest { cmd.setFakeRuntime(); // Create package without Upgrade table - Document doc = XmlUtils.initDocumentBuilder().parse( - Files.newInputStream(TKit.SRC_ROOT.resolve( - "windows/classes/jdk/jpackage/internal/resources/main.wxs"))); + Document doc = XmlUtils.initDocumentBuilder().parse(TKit.SRC_ROOT.resolve( + "windows/classes/jdk/jpackage/internal/resources/main.wxs").toFile()); XPath xPath = XPathFactory.newInstance().newXPath(); NodeList nodes = (NodeList) xPath.evaluate("/Wix/Product/Upgrade", doc, XPathConstants.NODESET); diff --git a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java b/test/jdk/tools/jpackage/windows/WinNoRestartTest.java index 909ee06b01a..7390b2f79a3 100644 --- a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java +++ b/test/jdk/tools/jpackage/windows/WinNoRestartTest.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 @@ -21,15 +21,18 @@ * questions. */ -import static jdk.jpackage.test.WindowsHelper.killAppLauncherProcess; import java.io.IOException; import java.time.Duration; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CfgFile; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; /* @test * @bug 8340311 @@ -47,7 +50,7 @@ import jdk.jpackage.test.JPackageCommand; public class WinNoRestartTest { @Test - public static void test() throws InterruptedException, IOException { + public static void test() throws InterruptedException, IOException, ExecutionException { var cmd = JPackageCommand.helloAppImage().ignoreFakeRuntime(); // Configure test app to launch in a way it will not exit @@ -77,7 +80,7 @@ public class WinNoRestartTest { private static record NoRerunConfig(NoRerunSectionConfig firstSection, NoRerunSectionConfig secondSection, boolean expectedNoRestarted) { - void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException { + void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException, ExecutionException { // Alter the main launcher .cfg file var cfgFile = new CfgFile(); if (firstSection != null) { @@ -92,16 +95,43 @@ public class WinNoRestartTest { // Save updated main launcher .cfg file cfgFile.save(cmd.appLauncherCfgPath(null)); + var state = TKit.state(); + var f = new CompletableFuture(); + // Launch the app in a separate thread new Thread(() -> { - HelloApp.executeLauncher(cmd); + TKit.withState(() -> { + HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); + }, state); }).start(); - // Wait a bit to let the app start - Thread.sleep(Duration.ofSeconds(10)); + var mainLauncherProcess = f.get(); - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, expectedNoRestarted ? 1 : 2); + try { + // Wait a bit to let the app start + Thread.sleep(Duration.ofSeconds(10)); + + try (var children = mainLauncherProcess.children()) { + Optional childPid = children.filter(p -> { + return mainLauncherProcess.info().command().equals(p.info().command()); + }).map(ProcessHandle::pid).map(Object::toString).findFirst(); + + Optional expectedChildPid; + if (expectedNoRestarted) { + expectedChildPid = Optional.empty(); + } else { + expectedChildPid = childPid.or(() -> { + return Optional.of(""); + }); + } + TKit.assertEquals(expectedChildPid, childPid, String.format( + "Check the main launcher process with PID=%d restarted", + mainLauncherProcess.pid())); + } + } finally { + TKit.trace("About to kill the main launcher process..."); + mainLauncherProcess.destroyForcibly(); + } } } diff --git a/test/langtools/jdk/jshell/ErrorTranslationTest.java b/test/langtools/jdk/jshell/ErrorTranslationTest.java index 1aa1b2c41dd..235f4443004 100644 --- a/test/langtools/jdk/jshell/ErrorTranslationTest.java +++ b/test/langtools/jdk/jshell/ErrorTranslationTest.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 @@ -149,7 +149,6 @@ public class ErrorTranslationTest extends ReplToolTesting { return sb.toString(); } - @Test public String getKind(Diagnostic.Kind kind) { switch (kind) { case WARNING: diff --git a/test/langtools/jdk/jshell/IdGeneratorTest.java b/test/langtools/jdk/jshell/IdGeneratorTest.java index 6c7f6177e03..521c87b0265 100644 --- a/test/langtools/jdk/jshell/IdGeneratorTest.java +++ b/test/langtools/jdk/jshell/IdGeneratorTest.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 @@ -44,7 +44,6 @@ import org.junit.jupiter.api.Test; public class IdGeneratorTest { - @Test public JShell.Builder getBuilder() { TestingInputStream inStream = new TestingInputStream(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); diff --git a/test/langtools/jdk/jshell/KullaCompletenessStressTest.java b/test/langtools/jdk/jshell/KullaCompletenessStressTest.java index 69830880dcb..16274fa924e 100644 --- a/test/langtools/jdk/jshell/KullaCompletenessStressTest.java +++ b/test/langtools/jdk/jshell/KullaCompletenessStressTest.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 @@ -32,13 +32,11 @@ import java.io.File; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class KullaCompletenessStressTest extends CompletenessStressTest { @Override - @Test public File[] getDirectoriesToTest() { String src = System.getProperty("test.src"); File file; diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java index 803e0c8865b..a65c4503238 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java @@ -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 @@ -44,7 +44,8 @@ public class TestNewCastArray { // 'b' tests fail with only even numbers of annotations (8005681). String[] testclasses = {"Test1", "Test2a", "Test3a", "Test4a", "Test5a", - "Test2b", "Test3b", "Test4b", "Test5b" + "Test2b", "Test3b", "Test4b", "Test5b", + "Test6a" }; public static void main(String[] args) throws Exception { @@ -182,6 +183,11 @@ public class TestNewCastArray { case "ci2": expected = 0; break; case "ci22": expected = 0; break; + case "Test6a": cexpected=4; break; + case "test6aPrimitiveArray": expected = 0; break; + case "test6aRefArray": expected = 0; break; + case "test6aMethod": cexpected = 4; break; + default: expected = 0; break; } if(codeattr) @@ -353,6 +359,18 @@ public class TestNewCastArray { Integer ci22 = (@A @A @B @B Integer)o; // FAIL expect 3, got 1 } + static class Test6a { + Test6a(){} + long l = 0; + // Cast expressions inside new array dimensions: + int[] test6aPrimitiveArray = new int[(@A @A @B @B int) l]; + Integer[] test6aRefArray = new Integer[(@A @A @B @B int) l]; + private void test6aMethod() { + int[] primitiveArray = new int[(@A @A @B @B int) l]; + Integer[] refArray = new Integer[(@A @A @B @B int) l]; + } + } + @Retention(RUNTIME) @Target({TYPE_USE}) @Repeatable( AC.class ) @interface A { } @Retention(RUNTIME) @Target({TYPE_USE}) @Repeatable( BC.class ) @interface B { } @Retention(RUNTIME) @Target({FIELD}) @Repeatable( FC.class ) @interface F { } diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index fd244c6acc6..18356dd8aa9 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.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 @@ -57,6 +57,8 @@ abstract public class CDSAppTester { private int numProductionRuns = 0; private String whiteBoxJar = null; private boolean inOneStepTraining = false; + private boolean generateBaseArchive = false; + private String[] baseArchiveOptions = new String[0]; /** * All files created in the CDS/AOT workflow will be name + extension. E.g. @@ -343,7 +345,7 @@ abstract public class CDSAppTester { // VM options used by this test, we need to create a temporary static archive to be used with -XX:ArchiveClassesAtExit. private String getBaseArchiveForDynamicArchive() throws Exception { WhiteBox wb = WhiteBox.getWhiteBox(); - if (wb.isSharingEnabled()) { + if (wb.isSharingEnabled() && !generateBaseArchive) { // This current JVM is able to use a default CDS archive included by the JDK, so // if we launch a JVM child process (with the same set of options as the current JVM), // that process is also able to use the same default CDS archive for creating @@ -356,6 +358,7 @@ abstract public class CDSAppTester { if (!f.exists()) { CDSOptions opts = new CDSOptions(); opts.setArchiveName(tempBaseArchiveFile); + opts.addSuffix(baseArchiveOptions); opts.addSuffix("-Djava.class.path="); OutputAnalyzer out = CDSTestUtils.createArchive(opts); CDSTestUtils.checkBaseDump(out); @@ -364,6 +367,16 @@ abstract public class CDSAppTester { } } + public CDSAppTester setGenerateBaseArchive(boolean b) { + this.generateBaseArchive = b; + return this; + } + + public CDSAppTester setBaseArchiveOptions(String... opts) { + this.baseArchiveOptions = opts; + return this; + } + private OutputAnalyzer dumpDynamicArchive() throws Exception { RunMode runMode = RunMode.DUMP_DYNAMIC; String[] cmdLine = new String[0]; diff --git a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java index c869cfa366b..a8bba9c76e2 100644 --- a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.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,9 +58,11 @@ public class SimpleCDSAppTester { private String modulepath; private String[] appCommandLine; private String[] vmArgs = new String[] {}; + private Tester tester; private SimpleCDSAppTester(String name) { this.name = name; + this.tester = new Tester(name); } public static SimpleCDSAppTester of(String name) { @@ -101,6 +103,16 @@ public class SimpleCDSAppTester { return this; } + public SimpleCDSAppTester setGenerateBaseArchive(boolean b) { + tester.setGenerateBaseArchive(b); + return this; + } + + public SimpleCDSAppTester setBaseArchiveOptions(String... opts) { + tester.setBaseArchiveOptions(opts); + return this; + } + public SimpleCDSAppTester setTrainingChecker(BiConsumer checker) { this.trainingChecker = checker; return this; @@ -181,17 +193,22 @@ public class SimpleCDSAppTester { } public SimpleCDSAppTester runStaticWorkflow() throws Exception { - (new Tester(name)).runStaticWorkflow(); + tester.runStaticWorkflow(); + return this; + } + + public SimpleCDSAppTester runDynamicWorkflow() throws Exception { + tester.runDynamicWorkflow(); return this; } public SimpleCDSAppTester runAOTWorkflow() throws Exception { - (new Tester(name)).runAOTWorkflow(); + tester.runAOTWorkflow(); return this; } public SimpleCDSAppTester run(String args[]) throws Exception { - (new Tester(name)).run(args); + tester.run(args); return this; } } diff --git a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp index 11600bd524b..3832d934b1e 100644 --- a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp +++ b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp @@ -123,6 +123,12 @@ char* julong_to_string(julong value, char *string) { return string; } +static void +fatal(const char* msg) { + LOG("FATAL ERROR: %s\n", msg); + abort(); +} + static void fatal(JNIEnv* jni, const char* msg) { jni->FatalError(msg); @@ -313,6 +319,17 @@ get_thread_name(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) { return tname; } +static char* +get_method_name(jvmtiEnv *jvmti, jmethodID method) { + char* mname = nullptr; + jvmtiError err; + + err = jvmti->GetMethodName(method, &mname, nullptr, nullptr); + check_jvmti_error(err, "get_method_name: error in JVMTI GetMethodName call"); + + return mname; +} + static char* get_method_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) { char* mname = nullptr; diff --git a/test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java b/test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java new file mode 100644 index 00000000000..c91b3e24ba7 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java @@ -0,0 +1,161 @@ +/* + * 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.awt.image; + +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Benchmark; +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.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Hashtable; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 20) +@Fork(3) +@State(Scope.Thread) +public class PNGImageDecoder_8bit_uninterlaced { + + byte[] pngImageData; + + @Setup + public void setup() throws Exception { + pngImageData = createImageData(2_500); + } + + @Benchmark + public void measurePNGImageDecoder(Blackhole bh) throws Exception { + Image img = Toolkit.getDefaultToolkit().createImage(pngImageData); + BufferedImage bi = createBufferedImage(img); + bi.flush(); + bh.consume(bi); + } + + /** + * Create a large sample image stored as an 8-bit PNG. + * + * @return the byte representation of the PNG image. + */ + private static byte[] createImageData(int squareSize) throws Exception { + BufferedImage bi = new BufferedImage(squareSize, squareSize, + BufferedImage.TYPE_BYTE_INDEXED); + Random r = new Random(0); + Graphics2D g = bi.createGraphics(); + for (int a = 0; a < 20000; a++) { + g.setColor(new Color(r.nextInt(0xffffff))); + int radius = 10 + r.nextInt(90); + g.fillOval(r.nextInt(bi.getWidth()), r.nextInt(bi.getHeight()), + radius, radius); + } + g.dispose(); + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + ImageIO.write(bi, "png", out); + return out.toByteArray(); + } + } + + static BufferedImage createBufferedImage(Image img) + throws ExecutionException, InterruptedException { + CompletableFuture future = new CompletableFuture<>(); + img.getSource().startProduction(new ImageConsumer() { + private int imageWidth, imageHeight; + private BufferedImage bi; + + @Override + public void setDimensions(int width, int height) { + imageWidth = width; + imageHeight = height; + } + + @Override + public void setProperties(Hashtable props) { + // intentionally empty + } + + @Override + public void setColorModel(ColorModel model) { + // intentionally empty + } + + @Override + public void setHints(int hintflags) { + // intentionally empty + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) { + if (bi == null) { + bi = new BufferedImage(imageWidth, imageHeight, + BufferedImage.TYPE_BYTE_INDEXED, + (IndexColorModel) model); + } + if (h != 1) + throw new UnsupportedOperationException( + "this test expects sequential rows of pixels"); + if (off != 0) + throw new UnsupportedOperationException( + "this test expects the incoming pixels to start " + + "at index zero"); + + bi.getRaster().setDataElements(x, y, w, 1, pixels); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) { + throw new UnsupportedOperationException(); + } + + @Override + public void imageComplete(int status) { + future.complete(bi); + } + }); + return future.get(); + } +} \ No newline at end of file diff --git a/test/micro/org/openjdk/bench/java/io/FileWrite.java b/test/micro/org/openjdk/bench/java/io/FileWrite.java index 21b0c2f8f54..c5a5aee70d8 100644 --- a/test/micro/org/openjdk/bench/java/io/FileWrite.java +++ b/test/micro/org/openjdk/bench/java/io/FileWrite.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 @@ -25,6 +25,7 @@ package org.openjdk.bench.java.io; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.RandomAccessFile; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -81,4 +82,29 @@ public class FileWrite { } } + @State(Scope.Benchmark) + @Warmup(iterations = 3, time = 2) + @Measurement(iterations = 5, time = 5) + @BenchmarkMode(Mode.SampleTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @Threads(1) + @Fork(value = 10) + public static class OpenFileForWritingBench { + final byte[] payload = "something".getBytes(); + final String path = System.getProperty("os.name", "unknown").toLowerCase().contains("win") ? "NUL" : "/dev/null"; + + @Benchmark + public void testFileOutputStream() throws IOException { + try (FileOutputStream f = new FileOutputStream(path)) { + f.write(payload); + } + } + + @Benchmark + public void testRandomAccessFile() throws IOException { + try (RandomAccessFile f = new RandomAccessFile(path, "rw")) { + f.write(payload); + } + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java new file mode 100644 index 00000000000..ba559b52344 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.foreign; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.nio.charset.StandardCharsets.UTF_8; + +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.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3) +public class FromJavaStringTest { + + private String str; + private MemorySegment strSegment; + private int lengthBytes; + + @Param({"5", "20", "100", "200", "451"}) + int size; + + @Setup + public void setup() { + var arena = Arena.ofAuto(); + while (LOREM.length() < size) { + LOREM += LOREM; + } + str = LOREM.substring(0, size); + strSegment = arena.allocateFrom(str); + lengthBytes = str.getBytes(UTF_8).length; + } + + @Benchmark + public void segment_setString() { + strSegment.setString(0, str, UTF_8); + } + + @Benchmark + public void segment_copyStringRaw() { + MemorySegment.copy(str, UTF_8, 0, strSegment, 0, str.length()); + } + + @Benchmark + public void segment_copyStringBytes() { + byte[] bytes = str.getBytes(UTF_8); + MemorySegment.copy(bytes, 0, strSegment, JAVA_BYTE, 0, bytes.length); + } + + static String LOREM = + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt + mollit anim id est laborum. + """; +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java index 901f4c7097f..c3e8f3aaca4 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java @@ -22,6 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.nio.charset.StandardCharsets.UTF_8; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -47,6 +50,7 @@ import java.util.concurrent.TimeUnit; public class ToJavaStringTest { private MemorySegment strSegment; + private int length; @Param({"5", "20", "100", "200", "451"}) int size; @@ -61,19 +65,33 @@ public class ToJavaStringTest { while (LOREM.length() < size) { LOREM += LOREM; } - strSegment = arena.allocateFrom(LOREM.substring(0, size)); + var s = LOREM.substring(0, size); + strSegment = arena.allocateFrom(s); + length = s.getBytes(UTF_8).length; } @Benchmark - public String panama_readString() { + public String segment_getString() { return strSegment.getString(0); } + @Benchmark + public String segment_getStringLength() { + return strSegment.getString(0, UTF_8, length); + } + @Benchmark public String jni_readString() { return readString(strSegment.address()); } + @Benchmark + public String segment_copyStringBytes() { + byte[] bytes = new byte[length]; + MemorySegment.copy(strSegment, JAVA_BYTE, 0, bytes, 0, length); + return new String(bytes, UTF_8); + } + static native String readString(long addr); static String LOREM = """